我們介紹多線程首先我們需要一些基礎知識一下我們一一介紹
進程:指在系統中正在運行的一個應用程序,每個進程是獨立,每個京城都運行在其專用受保護的內存空間。這也就是說多進程要比多線程健壯我們之后介紹。
線程:是進程的基本執行單元,一個進程的所有任務都在線程中執行,在一個線程中任務都是串行(順序執行)。
線程通信:在一個進程中,線程往往不是孤立存在的,多個線程之間需要經常進行通信,比如一個線程傳遞數據給另一個線程,執行完一個線程的任務后在執行另一個線程。
多線程:一個進程中開啟多條線程,每條線程并發(同時)執行不同的任務,多線程可以提高程序的執行效率。
原理
同一時間,CPU只能處理1條線程,只有1條線程在工作(執行),多線程并發(同時)執行,其實是CPU快速地在多條線程之間調度(切換),如果CPU調度線程的時間足夠快,就造成了多線程并發執行的假象。同樣如果頻繁的切換線程,使得CPU在N個線程之間調度,消耗大量的的CPU資源使得線程的執行效率降低,多線程開發我們要找到平衡點,控制線程的數量。
優點
提高程序的執行效率,不會因為一些耗時操作影響其他功能正常運行同時還可以提高資源利用率,CPU和內存的利用率。
缺點
開啟線程會占用一定的內存空間,如果大量的開啟線程會占用大量的內存空間,同時CPU在調度線程上也會占用資源,在程序設計會變得復雜,線程的通信和數據的共享等等。
iOS的多線程的實現目前有4種:Pthread, NSThread, GCD, NSOperation,以下我們分別介紹。
Pthread是一套通用的API,多個平臺支持,它是基于C語言的,線程的生命周期需要我們手動管理,使用難度大,平時開發基本不用。我們還是來一個簡單的例子
//創建一個線程并自動執行 pthread_create(&thread, NULL, start, NULL);NSThread是Objective-C對C語言Pthread的封裝,面向對象簡單易用,可直接操作線程對象,同樣生命周期需要我們管理。開發中不常使用,以下是對NSThread方法得說明。
創建、啟動線程NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];[thread start];主線程相關用法+ (NSThread *)mainThread; // 獲得主線程- (BOOL)isMainThread; // 是否為主線程+ (BOOL)isMainThread; // 是否為主線程獲得當前線程 NSThread *current = [NSThread currentThread];線程的名字- (void)setName:(NSString *)n;- (NSString *)name;線程的調度優先級,調度優先級的取值范圍是0.0 ~ 1.0,默認0.5,值越大,優先級越高+ (double)threadPRiority;+ (BOOL)setThreadPriority:(double)p;- (double)threadPriority;- (BOOL)setThreadPriority:(double)p;創建線程后自動啟動線程[NSThread detachNewThreadSelector:@selector(test) toTarget:self withObject:nil];隱式創建并啟動線程[self performSelectorInBackground:@selector(test) withObject:nil];控制線程狀態//啟動線程- (void)start; //阻塞(暫停)線程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;//強制停止線程+ (void)exit;//線程停止了,就不能再次開啟任務-線程間通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;GCD全稱是Grand Central Dispatch,蘋果公司為多核的并行運算提出的解決方案,會自動利用更多的CPU內核(比如雙核、四核),會自動管理線程的生命周期(創建線程、調度任務、銷毀線程),雖然是純C語言,基于Block實現,但是使用起來非常方便。想要更深的了解GCD就必須知道任務和隊列。
任務:就是需要執行的操作,我們可以定制任務,任務的執行分為同步執行和異步執行,區別在于是否開啟新的線程。
同步執行
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);異步執行
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);隊列:就是用來存放任務,把我們定制的任務存放在隊列中,GCD會自動將隊列中的任務取出,放到對應的線程中執行,任務的取出遵循隊列FIFO原則:先進先出,后進后出。隊列分為兩大類型:并發隊列和串行隊列。
并發隊列 可以讓多個任務并發(同時)執行(自動開啟多個線程同時執行任務)并發功能只有在異步(dispatch_async)函數下才有效,GCD默認已經提供了全局的并發隊列,供整個應用使用,不需要手動創建
使用dispatch_get_global_queue函數獲得全局的并發隊列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority, // 隊列的優先級unsigned long flags); // 此參數暫時無用,用0即可dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全局并發隊列/*全局并發隊列的優先級#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默認(中)#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后臺*/串行隊列 讓任務一個接著一個地執行(一個任務執行完畢后,再執行下一個任務),GCD中獲得串行有2種
1.使用dispatch_queue_create函數創建串行隊列
dispatch_queue_tdispatch_queue_create(const char *label, // 隊列名稱 dispatch_queue_attr_t attr); // 隊列屬性,一般用NULL即可dispatch_queue_t queue = dispatch_queue_create("first", NULL); // 創建dispatch_release(queue); // 非ARC需要釋放手動創建的隊列2.使用主隊列(跟主線程相關聯的隊列),是GCD自帶的一種特殊的串行隊列,主隊列的任務都會放在主線程中執行,使用dispatch_get_main_queue()獲得主隊列
dispatch_queue_t queue = dispatch_get_main_queue();我們對任務同步異步和隊列串行并發的執行分析
串行隊列同步執行:不開線程,在原來的線程順序執行dispatch_queue_t queue = dispatch_queue_create("cs",DISPATCH_QUEUE_SERIAL);dispatch_sync(queue,^{ //串行隊列,同步執行,不開線程,順序執行});串行隊列異步執行:開一條線程,在線程中順序執行,效率低占用資源少(相對比較)dispatch_queue_t queue = dispatch_queue_create("cs",DISPATCH_QUEUE_SERIAL);dispatch_async(queue,^{ //串行隊列,異步執行,開一條線程,順序執行});并發隊列同步執行:不開線程,同步執行//并發隊列dispatch_queue_t queue = dispatch_queue_create("bs",DISPATHCH_QUEUE_CONCURRENT);dispatch_sync(queue,^{//先執行同步任務,不開線程});dispatch_async(queue,^{//等到同步任務執行完成后,開啟線程執行當前操作});dispatch_async(queue,^{//等到同步任務執行完成后,開啟線程執行當前操作});并發隊列異步執行:開第一個線程,并發執行,效率高占用資源大//并發隊列dispatch_queue_t queue = dispatch_queue_create("ba",DISPATHCH_QUEUE_CONCURRENT);dispatch_async(queue,^{//開啟線程執行當前操作});dispatch_async(queue,^{//開啟線程執行當前操作});1.調用NSObject方法
//2秒后調用[self performSelector:@selector(run) withObject:nil afterDelay:2.0];2.使用GCD函數
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后異步執行這里的代碼...});使用dispatch_once函數能保證某段代碼在程序運行過程中只被執行1次
static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ // 只執行1次的代碼(這里面默認是線程安全的)});可以管理組內的隊列執行情況,方便對線程管理,比如我們異步執行兩個耗時的操作,等待完成后回到主線程執行操作
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執行1個耗時的異步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 執行1個耗時的異步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的兩個異步操作都執行完畢后,回到主線程...});NSOperation:是基于GCD實現的,比GCD多了更加簡單實用的功能,更加面向對象。配合使用NSOperation和NSOperationQueue實現多線程編程,我們開發中經常使用。
提供兩種隊列:主隊列和全局并發隊列
主隊列
NSOperationQueue *queue = [[NSOperationQueue mainQueue];全局并發隊列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];添加任務
//1.添加一個NSOperation的子類- (void); addOperation:(NSOperation *)op[queue addOperation:op];//2.添加一個Block- (void)addOperationWithBlock:(void (^)(void))block;[queue addOperationWithBlock:^{ //代碼}];NSOperation是個抽象類,我們使用必須使用它的子類,使用NSOperation子類的方式有3種
1. NSInvocationOperation 對GCD中全局并發隊列的封裝
//1.創建操作NSOperation *op = [[NSInvocationOperation alloc] initWithTatger:self selecter:@Selector(downloadImage:) object:@"Invocation"];//2. 加入隊列中NSOperationQueue *queue = [[NSOperationQueue alloc]init];//異步執行[queue addOperation:op];2. NSBlockOperation對GCD中全局并發隊列的封裝
NSOperationQueue *queue = [[NSOperationQueue alloc]init];//添加任務NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ //需要執行的操作}];//添加到隊列[queue addOperation:op];3. 自定義子類繼承NSOperation,實現內部相應的方法
最大并發數就是同時執行的任務數,可以使用下面的方法
- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;取消
- (void)cancelAllOperations;//也可以調用NSOperation的- (void)cancel方法取消單個操作暫停/恢復
// YES代表暫停隊列,NO代表恢復隊列- (void)setSuspended:(BOOL)b; - (BOOL)isSuspended;設置NSOperation在queue中的優先級,可以改變操作的執行優先級
- (NSOperationQueuePriority)queuePriority;- (void)setQueuePriority:(NSOperationQueuePriority)p;/* 優先級的取值 NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8*/可以監聽一個操作的執行完畢
- (void (^)(void))completionBlock;- (void)setCompletionBlock:(void (^)(void))block;NSOperation之間可以設置依賴來保證執行順序,比如一定要讓操作A執行完后,才能執行操作B
// 操作B依賴于操作A[operationB addDependency:operationA];我們之前提到的原子性和非原子性,相信在這次的介紹中已經有根深的理解了
1.資源共享:一個資源可能被多個線程共享,多線程訪問同一塊資源容易引發數據錯亂和數據安全
用來防止兩個進程或線程在同一時刻訪問相同的共享資源。
@synchronized(鎖對象) { // 需要鎖定的代碼 }優點:能有效防止因多線程搶奪資源造成的數據安全問題 缺點:需要消耗大量的CPU資源
死鎖: 是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。(在學校每次考試都考)
今天寫了好長時間,總結整理,眼睛感覺已經不是自己的了,發現有不對的地方歡迎評論和聯系微博
新聞熱點
疑難解答