前言:
在使用GCD多線程做操作時始绍,有些時候需要咱們多線程里面的內容全部完成以后话侄,再去刷新頁面內容年堆,這種情況下就需要咱們知道什么時候多線程隊列里面的內容已經執(zhí)行完畢,這里列舉三種方法变丧,希望能對有需要的小伙伴有啟發(fā)痒蓬。
一:信號量
信號量是一個整形值并且具有一個初始計數(shù)值,并且支持兩個操作:信號通知和等待柔昼。當一個信號量被信號通知炎辨,其計數(shù)會被增加聪姿。當一個線程在一個信號量上等待時末购,線程會被阻塞(如果有必要的話),直至計數(shù)器大于零盟榴,然后線程會減少這個計數(shù)。
在GCD中有三個函數(shù)是semaphore的操作擎场,分別是:
dispatch_semaphore_create 創(chuàng)建一個semaphore
dispatch_semaphore_signal 發(fā)送一個信號
dispatch_semaphore_wait 等待信號
第一個函數(shù)是創(chuàng)建一個信號量迅办,我們會給它一個初始值,第二個函數(shù)是發(fā)送信號量姨夹,當調用該函數(shù)的時候,信號量的值會加一峭沦,第三方函數(shù)是等待信號量逃糟,調用等待信號量以后信號量的值會減一,并且當信號量的值小于0時蛉抓,線程就會一直等待剃诅,直到信號量的值大于等于0時才能繼續(xù)執(zhí)行矛辕。
咱們下面逐一介紹這三個函數(shù)的調用以及參數(shù)意義
1.dispatch_semaphore_create
dispatch_samaphore_t dispatch_semaphore_create(long value);
傳入的參數(shù)為long,輸出一個dispatch_semaphore_t類型且值為value的信號量飞蹂。
值得注意的是翻屈,這里的傳入的參數(shù)value必須大于或等于0伸眶,否則dispatch_semaphore_create會返回NULL。
2.dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_tdsema)
這里的參數(shù)是第一個函數(shù)創(chuàng)建的信號
-
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)界酒;第一參數(shù)的是信號嘴秸,第二個參數(shù)是超時時間的設定岳掐,系統(tǒng)給我們了兩 種超時時間設定,一種是 DISPATCH_TIME_NOW 表示當前哭尝; DISPATCH_TIME_FOREVER 表示遙遠的未來剖煌;你可以選擇其中一種也可以自己創(chuàng)建一個超時時間函數(shù)
創(chuàng)建超時timeout有兩種方法
第一種方法:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta)逝淹;
其參數(shù)when需傳入一個dispatch_time_t類型的變量栅葡,和一個delta值尤泽。表示when加delta時間就是timeout的時間。
第二種方式:
dispatch_time_t dispatch_walltime(<#const struct timespec * _Nullable when, int64_t delta);
其參數(shù)when需傳入一個dispatch_time_t類型的變量熊咽,和一個delta值闹丐。表示when加delta時間就是timeout的時間卿拴。
6.關于信號量,一般可以用停車來比喻文狱。
網(wǎng)吧剩余6個座位缘挽,那么即使同時來了6個人也能同時接待。如果此時來了7個人杠袱,那么就有一個人需要等待窝稿。
信號量的值就相當于剩余座位的數(shù)目凿掂,dispatch_semaphore_wait函數(shù)就相當于來了一個人,dispatch_semaphore_signal
就相當于走了一個人庄萎。網(wǎng)絡座位的剩余數(shù)目在初始化的時候就已經指明了(dispatch_semaphore_create(long value))糠涛,
調用一次dispatch_semaphore_signal,剩余的座位就增加一個集漾;調用一次dispatch_semaphore_wait剩余座位就減少一個具篇;
當剩余座位為0時,再來人(即調用dispatch_semaphore_wait)就只能等待驱显。有可能同時有幾個人等待一個座位。有些人
沒有耐心埃疫,給自己設定了一段等待時間伏恐,這段時間內等不到座位就走了,如果等到了就去上網(wǎng)栓霜。而有些人就想站在這里脐湾,所以就一直等下去。
具體到代碼中
假設咱們現(xiàn)在需要做完A叙淌、B秤掌、事件以后才能去做C事件那么咱們就可以仿照下面的做法
#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initlizeAppeareces];
}
- (void)initlizeAppeareces{
//crate的value表示,最多幾個資源可訪問
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"辦理A事件");
dispatch_semaphore_signal(semaphore);
});
//任務2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"辦理B事件");
dispatch_semaphore_signal(semaphore);
});
}
當咱們信號量為2時鹰霍,證明之前的事件都辦理完了闻鉴,咱們就可以做其他的事情了。
二:隊列加group分組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后孟岛,回到主線程...
});
當程序執(zhí)行dispatch_group_notify()函數(shù)時,證明隊列中的任務已經執(zhí)行完了督勺。
第三種方式dispatch_barrier_sync和dispatch_barrier_async
共同點:
1渠羞、等待在它前面插入隊列的任務先執(zhí)行完
2、等待他們自己的任務執(zhí)行完再執(zhí)行后面的任務
不同點:
1智哀、dispatch_barrier_sync將自己的任務插入到隊列的時候次询,需要等待自己的任務結束之后才會繼續(xù)插入被寫在它后面的任務,然后執(zhí)行它們
2瓷叫、dispatch_barrier_async將自己的任務插入到隊列之后屯吊,不會等待自己的任務結束,它會繼續(xù)把后面的任務插入到隊列摹菠,然后等待自己的任務結束后才執(zhí)行后面任務盒卸。
所以調用這兩種方法中的其中一個都會等前面的方法執(zhí)行完以后再執(zhí)行之后的方法,就可以確定隊列中任務是否完成
注: 在信號量的demo編寫過程中我發(fā)現(xiàn)當信號量等于0的時候都崩潰了,如果有知道原因的老鐵歡迎前來分享次氨。
四:將任務添加到串行隊列中
除了像上面那樣處理蔽介,也可以把任務放到各個串行隊列中,并用dispatch group跟蹤其執(zhí)行狀況。然而虹蓄,如果所有任務都排在同一個串行隊列里面犀呼,那么dispatch group就用處不大了。因為此時任務總要逐個執(zhí)行武花,所以只需要在提交完全部任務之后再提交一個塊即可圆凰,這樣做與通過notify函數(shù)等待dispatch group執(zhí)行完畢然后再回調塊是等效的:
dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivec.queue",NULL);
for(id object in collection){
dispatch_async(queue,^{
[object performTask];
});
}
dispatch_async(queue,^{
//Continue processing after completing tasks
});
上面這段代碼表明,開發(fā)者未必總需要使用dispatch group体箕。有時候采用單個隊列搭配標準的異步派發(fā)专钉,也可以實現(xiàn)同樣的效果。
筆者為何要在標題中談到"根據(jù)系統(tǒng)資源狀況來執(zhí)行任務"呢累铅?回頭看看向并發(fā)隊列派發(fā)任務的那個例子跃须,就會明白了。為了執(zhí)行隊列中的塊娃兽,GCD會在適當?shù)膶嶋H自動創(chuàng)建新線程菇民。