前言:
在使用GCD多線程做操作時(shí),有些時(shí)候需要咱們多線程里面的內(nèi)容全部完成以后,再去刷新頁面內(nèi)容,這種情況下就需要咱們知道什么時(shí)候多線程隊(duì)列里面的內(nèi)容已經(jīng)執(zhí)行完畢,這里列舉三種方法,希望能對有需要的小伙伴有啟發(fā)。
一:信號量
信號量是一個(gè)整形值并且具有一個(gè)初始計(jì)數(shù)值,并且支持兩個(gè)操作:信號通知和等待。當(dāng)一個(gè)信號量被信號通知,其計(jì)數(shù)會(huì)被增加。當(dāng)一個(gè)線程在一個(gè)信號量上等待時(shí),線程會(huì)被阻塞(如果有必要的話),直至計(jì)數(shù)器大于零,然后線程會(huì)減少這個(gè)計(jì)數(shù)。
在GCD中有三個(gè)函數(shù)是semaphore的操作,分別是:
dispatch_semaphore_create 創(chuàng)建一個(gè)semaphore
dispatch_semaphore_signal 發(fā)送一個(gè)信號
dispatch_semaphore_wait 等待信號
第一個(gè)函數(shù)是創(chuàng)建一個(gè)信號量,我們會(huì)給它一個(gè)初始值,第二個(gè)函數(shù)是發(fā)送信號量,當(dāng)調(diào)用該函數(shù)的時(shí)候,信號量的值會(huì)加一,第三方函數(shù)是等待信號量,調(diào)用等待信號量以后信號量的值會(huì)減一,并且當(dāng)信號量的值小于0時(shí),線程就會(huì)一直等待,直到信號量的值大于等于0時(shí)才能繼續(xù)執(zhí)行。
咱們下面逐一介紹這三個(gè)函數(shù)的調(diào)用以及參數(shù)意義
1.dispatch_semaphore_create
dispatch_samaphore_t dispatch_semaphore_create(long value);
傳入的參數(shù)為long,輸出一個(gè)dispatch_semaphore_t類型且值為value的信號量。
值得注意的是,這里的傳入的參數(shù)value必須大于或等于0,否則dispatch_semaphore_create會(huì)返回NULL。
2.dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_tdsema)
這里的參數(shù)是第一個(gè)函數(shù)創(chuàng)建的信號
-
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);第一參數(shù)的是信號,第二個(gè)參數(shù)是超時(shí)時(shí)間的設(shè)定,系統(tǒng)給我們了兩 種超時(shí)時(shí)間設(shè)定,一種是 DISPATCH_TIME_NOW 表示當(dāng)前; DISPATCH_TIME_FOREVER 表示遙遠(yuǎn)的未來;你可以選擇其中一種也可以自己創(chuàng)建一個(gè)超時(shí)時(shí)間函數(shù)
創(chuàng)建超時(shí)timeout有兩種方法
第一種方法:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);
其參數(shù)when需傳入一個(gè)dispatch_time_t類型的變量,和一個(gè)delta值。表示when加delta時(shí)間就是timeout的時(shí)間。
第二種方式:
dispatch_time_t dispatch_walltime(<#const struct timespec * _Nullable when, int64_t delta);
其參數(shù)when需傳入一個(gè)dispatch_time_t類型的變量,和一個(gè)delta值。表示when加delta時(shí)間就是timeout的時(shí)間。
6.關(guān)于信號量,一般可以用停車來比喻。
網(wǎng)吧剩余6個(gè)座位,那么即使同時(shí)來了6個(gè)人也能同時(shí)接待。如果此時(shí)來了7個(gè)人,那么就有一個(gè)人需要等待。
信號量的值就相當(dāng)于剩余座位的數(shù)目,dispatch_semaphore_wait函數(shù)就相當(dāng)于來了一個(gè)人,dispatch_semaphore_signal
就相當(dāng)于走了一個(gè)人。網(wǎng)絡(luò)座位的剩余數(shù)目在初始化的時(shí)候就已經(jīng)指明了(dispatch_semaphore_create(long value)),
調(diào)用一次dispatch_semaphore_signal,剩余的座位就增加一個(gè);調(diào)用一次dispatch_semaphore_wait剩余座位就減少一個(gè);
當(dāng)剩余座位為0時(shí),再來人(即調(diào)用dispatch_semaphore_wait)就只能等待。有可能同時(shí)有幾個(gè)人等待一個(gè)座位。有些人
沒有耐心,給自己設(shè)定了一段等待時(shí)間,這段時(shí)間內(nèi)等不到座位就走了,如果等到了就去上網(wǎng)。而有些人就想站在這里,所以就一直等下去。
具體到代碼中
假設(shè)咱們現(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表示,最多幾個(gè)資源可訪問
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"辦理A事件");
dispatch_semaphore_signal(semaphore);
});
//任務(wù)2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"辦理B事件");
dispatch_semaphore_signal(semaphore);
});
}
當(dāng)咱們信號量為2時(shí),證明之前的事件都辦理完了,咱們就可以做其他的事情了。
二:隊(duì)列加group分組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
當(dāng)程序執(zhí)行dispatch_group_notify()函數(shù)時(shí),證明隊(duì)列中的任務(wù)已經(jīng)執(zhí)行完了。
第三種方式dispatch_barrier_sync和dispatch_barrier_async
共同點(diǎn):
1、等待在它前面插入隊(duì)列的任務(wù)先執(zhí)行完
2、等待他們自己的任務(wù)執(zhí)行完再執(zhí)行后面的任務(wù)
不同點(diǎn):
1、dispatch_barrier_sync將自己的任務(wù)插入到隊(duì)列的時(shí)候,需要等待自己的任務(wù)結(jié)束之后才會(huì)繼續(xù)插入被寫在它后面的任務(wù),然后執(zhí)行它們
2、dispatch_barrier_async將自己的任務(wù)插入到隊(duì)列之后,不會(huì)等待自己的任務(wù)結(jié)束,它會(huì)繼續(xù)把后面的任務(wù)插入到隊(duì)列,然后等待自己的任務(wù)結(jié)束后才執(zhí)行后面任務(wù)。
所以調(diào)用這兩種方法中的其中一個(gè)都會(huì)等前面的方法執(zhí)行完以后再執(zhí)行之后的方法,就可以確定隊(duì)列中任務(wù)是否完成
注: 在信號量的demo編寫過程中我發(fā)現(xiàn)當(dāng)信號量等于0的時(shí)候都崩潰了,如果有知道原因的老鐵歡迎前來分享。
四:將任務(wù)添加到串行隊(duì)列中
除了像上面那樣處理,也可以把任務(wù)放到各個(gè)串行隊(duì)列中,并用dispatch group跟蹤其執(zhí)行狀況。然而,如果所有任務(wù)都排在同一個(gè)串行隊(duì)列里面,那么dispatch group就用處不大了。因?yàn)榇藭r(shí)任務(wù)總要逐個(gè)執(zhí)行,所以只需要在提交完全部任務(wù)之后再提交一個(gè)塊即可,這樣做與通過notify函數(shù)等待dispatch group執(zhí)行完畢然后再回調(diào)塊是等效的:
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。有時(shí)候采用單個(gè)隊(duì)列搭配標(biāo)準(zhǔn)的異步派發(fā),也可以實(shí)現(xiàn)同樣的效果。
筆者為何要在標(biāo)題中談到"根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)"呢?回頭看看向并發(fā)隊(duì)列派發(fā)任務(wù)的那個(gè)例子,就會(huì)明白了。為了執(zhí)行隊(duì)列中的塊,GCD會(huì)在適當(dāng)?shù)膶?shí)際自動(dòng)創(chuàng)建新線程。