本文參考文章鏈接:
- 巧談GCD
- iOS多線程詳解
- iOS多線程——你要知道的GCD都在這里(這篇存在著一些錯誤,慎讀)
GCD3之dispatch_apply
dispatch_apply
1. 簡介
- dispatch_apply類似一個for循環(huán),會在指定的dispatch queue中運行block任務(wù)n次炼鞠。如果隊列是并發(fā)隊列桩撮,會開啟多線程垂谢,并發(fā)執(zhí)行block任務(wù)荸哟,儡炼;如果是串行隊列响牛,則會順序執(zhí)行玷禽。也就是說,dispatch_apply在并發(fā)隊列下呀打,有開啟多線程的能力矢赁。
- dispatch_apply函數(shù)在返回之前等待任務(wù)完成,block任務(wù)執(zhí)行n次后才返回贬丛,這點和同步執(zhí)行相同撩银。
- 使用dispatch_apply并發(fā)隊列,效率要高于for循環(huán)瘫寝。
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
參數(shù)說明:
第一個參數(shù):迭代次數(shù)
第二個參數(shù):隊列
block的參數(shù):(size_t 無符號整型)蜒蕾,隨便寫一個參數(shù)稠炬,應(yīng)該是為了并發(fā)時區(qū)分各個block用的
2. 例子,并發(fā)隊列:
全局并發(fā)隊列咪啡。index是隨便寫的
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%ld %@",index++,[NSThread currentThread]);
});
NSLog(@"done");
輸出結(jié)果:
2018-08-18 12:15:05.663343+0800 GCD[1117:113861] 0 <NSThread: 0x600000261c40>{number = 1, name = main}
2018-08-18 12:15:05.663348+0800 GCD[1117:113947] 2 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:15:05.663353+0800 GCD[1117:113945] 3 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663394+0800 GCD[1117:113946] 1 <NSThread: 0x60000047e3c0>{number = 3, name = (null)}
2018-08-18 12:15:05.663561+0800 GCD[1117:113945] 5 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663561+0800 GCD[1117:113947] 4 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:15:05.663563+0800 GCD[1117:113861] 6 <NSThread: 0x600000261c40>{number = 1, name = main}
2018-08-18 12:15:05.663563+0800 GCD[1117:113946] 7 <NSThread: 0x60000047e3c0>{number = 3, name = (null)}
2018-08-18 12:15:05.663672+0800 GCD[1117:113945] 8 <NSThread: 0x604000271ec0>{number = 5, name = (null)}
2018-08-18 12:15:05.663809+0800 GCD[1117:113947] 9 <NSThread: 0x604000272440>{number = 4, name = (null)}
2018-08-18 12:21:07.765380+0800 GCD[1134:118115] done
- index是從0開始的首启,因為是無符號整型。
- 輸出順序不一樣撤摸。因為是并發(fā)隊列毅桃,交給了多個線程去執(zhí)行。
- 最后才輸出done准夷。因為dispatch_apply必須等待任務(wù)完成才能返回钥飞。
3. 例子,串行隊列(并沒有什么用衫嵌,所以還是使用并行隊列去吧)
dispatch_queue_t serialQueue = dispatch_queue_create("xds, DISPATCH_QUEUE_SERIAL);
dispatch_apply(10, serialQueue, ^(size_t index) {
NSLog(@"%ld %@",index++,[NSThread currentThread]);
});
輸出結(jié)果:
2018-08-18 12:27:06.990331+0800 GCD[1166:122439] 0 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990560+0800 GCD[1166:122439] 1 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990678+0800 GCD[1166:122439] 2 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990793+0800 GCD[1166:122439] 3 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.990936+0800 GCD[1166:122439] 4 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991104+0800 GCD[1166:122439] 5 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991220+0800 GCD[1166:122439] 6 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991335+0800 GCD[1166:122439] 7 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991456+0800 GCD[1166:122439] 8 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991566+0800 GCD[1166:122439] 9 <NSThread: 0x6000000791c0>{number = 1, name = main}
2018-08-18 12:27:06.991735+0800 GCD[1166:122439] done
- 順序執(zhí)行读宙,因為是串行隊列,只能執(zhí)行完上一個任務(wù)楔绞,才能執(zhí)行下一個任務(wù)结闸。
- 最后輸出done。因為dispatch_apply等待任務(wù)完成才能返回酒朵。
- 只有一個主線程桦锄。
使用dispatch_apply串行隊列并沒有什么用,所以還是使用并行隊列去吧蔫耽。
4. 應(yīng)用方法
- 直接使用dispatch_apply并發(fā)隊列结耀。這樣會使用主線程處理一部分block。
- 完全使用子線程并發(fā)處理任務(wù):在異步執(zhí)行dispatch_asyn中使用dispatch_apply并發(fā)隊列匙铡,注意并發(fā)隊列不能是同一個图甜,否則只會使用同一個線程順序處理。
在某些場景下使用dispatch_apply會對性能有很大的提升鳖眼,比如你的代碼需要以每個像素為基準(zhǔn)來處理計算image圖片具则。同時dispatch apply能夠避免一些線程爆炸的情況發(fā)生(創(chuàng)建很多線程)。
5. 應(yīng)用場景
應(yīng)用場景1
可以用來拉取網(wǎng)絡(luò)數(shù)據(jù)后提前算出各個控件的大小具帮,防止繪制時計算博肋,提高表單滑動流暢性,如果用for循環(huán)蜂厅,耗時較多匪凡,并且每個表單的數(shù)據(jù)沒有依賴關(guān)系,所以用dispatch_apply比較好****
應(yīng)用場景2
如果我們從服務(wù)器獲取一個數(shù)組的數(shù)據(jù)掘猿,那么我們可以使用該方法從而****快速的批量字典轉(zhuǎn)模型病游,然后轉(zhuǎn)到主線程去更新UI****。
有一個字典數(shù)組,快速的字典轉(zhuǎn)模型衬衬。
- 不使用主線程买猖,完全使用子線程去處理。
注意:這樣要使用兩個不同的并發(fā)隊列滋尉,才能不使用主線程并且并發(fā)執(zhí)行玉控。
代碼示例如下:
NSArray *dictArray = nil;//存放從服務(wù)器返回的字典數(shù)組
dispatch_queue_t queue1 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue1, ^{
dispatch_apply(dictArray.count, queue, ^(size_t index){
//字典轉(zhuǎn)模型
NSLog(@"%ld %@", index++,[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主線程更新");
});
});
將dictArray.count用10代替,輸出結(jié)果如下狮惜。發(fā)現(xiàn)是多線程并發(fā)高诺,且沒有主線程參與。
2018-08-18 12:47:54.536758+0800 GCD[1226:136959] 3 <NSThread: 0x600000269380>{number = 6, name = (null)}
2018-08-18 12:47:54.536772+0800 GCD[1226:136956] 2 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.536779+0800 GCD[1226:136958] 1 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.536797+0800 GCD[1226:136960] 0 <NSThread: 0x600000268ec0>{number = 3, name = (null)}
2018-08-18 12:47:54.537036+0800 GCD[1226:136958] 5 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.537049+0800 GCD[1226:136956] 6 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.537060+0800 GCD[1226:136959] 4 <NSThread: 0x600000269380>{number = 6, name = (null)}
2018-08-18 12:47:54.537127+0800 GCD[1226:136960] 7 <NSThread: 0x600000268ec0>{number = 3, name = (null)}
2018-08-18 12:47:54.537287+0800 GCD[1226:136958] 8 <NSThread: 0x600000269300>{number = 4, name = (null)}
2018-08-18 12:47:54.538363+0800 GCD[1226:136956] 9 <NSThread: 0x6040004685c0>{number = 5, name = (null)}
2018-08-18 12:47:54.545784+0800 GCD[1226:136876] 主線程更新
- 可以使用主線程處理一部分任務(wù)碾篡。直接使用dispatch_apply.
NSArray *dictArray = nil;//存放從服務(wù)器返回的字典數(shù)組
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(dictArray.count, queue, ^(size_t index){
//字典轉(zhuǎn)數(shù)組
NSLog(@"%ld %@", index++,[NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"主線程更新");
});
將dictArray.count換成10虱而,輸出結(jié)果如下。發(fā)現(xiàn)是多線程并發(fā)开泽,且會有主線程在其中牡拇。
2018-08-18 12:54:50.402604+0800 GCD[1246:142090] 2 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.402604+0800 GCD[1246:142089] 3 <NSThread: 0x604000469040>{number = 4, name = (null)}
2018-08-18 12:54:50.402608+0800 GCD[1246:141938] 0 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.402655+0800 GCD[1246:142088] 1 <NSThread: 0x600000079380>{number = 3, name = (null)}
2018-08-18 12:54:50.402925+0800 GCD[1246:142090] 7 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.402926+0800 GCD[1246:141938] 6 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.402926+0800 GCD[1246:142088] 5 <NSThread: 0x600000079380>{number = 3, name = (null)}
2018-08-18 12:54:50.402945+0800 GCD[1246:142089] 4 <NSThread: 0x604000469040>{number = 4, name = (null)}
2018-08-18 12:54:50.403045+0800 GCD[1246:142090] 8 <NSThread: 0x604000468d00>{number = 5, name = (null)}
2018-08-18 12:54:50.403064+0800 GCD[1246:141938] 9 <NSThread: 0x600000068a40>{number = 1, name = main}
2018-08-18 12:54:50.407587+0800 GCD[1246:141938] 主線程更新
GCD4之dispatch_after
dispatch_after
1. 簡介
dispatch_after(dispatch_time_t when,dispatch_queue_t queue, dispatch_block_t block);
官方文檔
This function waits until the specified time and then asynchronously adds block to the specified queue.
Passing DISPATCH_TIME_NOW as the when parameter is supported, but is not as optimal as calling dispatch_async instead. Passing DISPATCH_TIME_FOREVER is undefined.
翻譯:
此函數(shù)等待直到指定的時間,然后異步地將塊添加到指定的隊列穆律。
傳遞DISPATCH_TIME_NOW作為支持的when參數(shù)诅迷,但不如調(diào)用dispatch_async那樣最優(yōu)。 傳遞DISPATCH_TIME_FOREVER未定義众旗。
- dispatch_after,是在指定時間安排一個任務(wù)提交給指定的隊列趟畏,而不是在指定時間去執(zhí)行任務(wù)贡歧;
- dispatch_after是一個異步執(zhí)行,也就是說提交任務(wù)后馬上返回赋秀,如果是串行隊列則一個個執(zhí)行利朵;如果是并發(fā)隊列則并發(fā)執(zhí)行;
- 因為是異步猎莲,有可能開啟多線程绍弟。
2. 一般用法
dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
// 在queue里面延遲執(zhí)行的一段代碼
...
});
3. 例子
在主隊列延遲提交一個任務(wù)。有可能開啟其它線程著洼。
NSLog(@"1111 %@", [NSThread currentThread]);
dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
NSLog(@"同步 %@", [NSThread currentThread]);
});
NSLog(@"2222 %@", [NSThread currentThread]);
2018-08-18 13:21:13.941606+0800 GCD[1278:159101] 1111 <NSThread: 0x604000073400>{number = 1, name = main}
2018-08-18 13:21:13.941915+0800 GCD[1278:159101] 2222 <NSThread: 0x604000073400>{number = 1, name = main}
2018-08-18 13:21:19.415315+0800 GCD[1278:159101] 同步 <NSThread: 0x604000073400>{number = 1, name = main}
4. 應(yīng)用場景
這為我們提供了一個簡單的延遲執(zhí)行的方式樟遣,比如在view加載結(jié)束延遲執(zhí)行一個動畫等等。
GCD5之dispatch_barrier_(a)sync
1. dispatch_barrier_async
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
官方文檔說明:
Submits a barrier block for asynchronous execution and returns immediately.
Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.
中文翻譯如下:
調(diào)用此函數(shù)后身笤,始終在提交塊后立即返回豹悬,并且永遠(yuǎn)不會等待調(diào)用該塊。 當(dāng)障礙塊到達(dá)專用并發(fā)隊列的前面時液荸,它不會立即執(zhí)行瞻佛。 相反,隊列等待娇钱,直到其當(dāng)前正在執(zhí)行的塊完成執(zhí)行伤柄。 此時绊困,屏障塊自行執(zhí)行。 阻擋塊之后提交的任何塊不會被執(zhí)行适刀,直到阻擋塊完成秤朗。
您指定的隊列應(yīng)該是您使用dispatch_queue_create函數(shù)自己創(chuàng)建的并發(fā)隊列。 如果傳遞給此函數(shù)的隊列是串行隊列或全局并發(fā)隊列之一蔗彤,則此函數(shù)的行為類似于dispatch_async函數(shù)川梅。
理解:
- 異步執(zhí)行,立即返回
- 相當(dāng)于一個承前啟后的作用然遏,它前面的執(zhí)行完成后贫途,執(zhí)行它,再執(zhí)行后面的任務(wù)
- 必須使用并發(fā)隊列待侵,且得是dispatch_queue_create自己創(chuàng)建的并發(fā)隊列丢早。串行隊列和全局隊列沒有用
- 實驗后得知,必須是同一個并發(fā)隊列的任務(wù)才會起到承前啟后的作用秧倾。
例子
// dispatch_barrier_async的作用可以用一個詞概括--承上啟下怨酝,它保證此前的任務(wù)都先于自己執(zhí)行,此后的任務(wù)也遲于自己執(zhí)行。本例中批糟,任務(wù)4會在任務(wù)1楞抡、2、3都執(zhí)行完之后執(zhí)行斤葱,而任務(wù)5、6會等待任務(wù)4執(zhí)行完后執(zhí)行揖闸。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 任務(wù)1
...
});
dispatch_async(queue, ^{
// 任務(wù)2
...
});
dispatch_async(queue, ^{
// 任務(wù)3
...
});
dispatch_barrier_async(queue, ^{
// 任務(wù)4
...
});
dispatch_async(queue, ^{
// 任務(wù)5
...
});
dispatch_async(queue, ^{
// 任務(wù)6
...
});
2. 應(yīng)用場景
和dispatch_group類似揍堕,dispatch_barrier也是異步任務(wù)間的一種同步方式,可以在比如文件的讀寫操作時使用汤纸,保證讀操作的準(zhǔn)確性衩茸。
3. dispatch_barrier_sync
和dispatch_barrier_async作用相同,只是有可能造成死鎖贮泞,要注意楞慈。
官方文檔
Submits a barrier block to a dispatch queue for synchronous execution. Unlike dispatch_barrier_async, this function does not return until the barrier block has finished. Calling this function and targeting the current queue results in deadlock.
When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_sync function.
Unlike with dispatch_barrier_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.
As an optimization, this function invokes the barrier block on the current thread when possible.
翻譯如下
將屏障塊提交到調(diào)度隊列以進(jìn)行同步執(zhí)行。與dispatch_barrier_async不同啃擦,此功能在屏障塊完成之前不會返回抖部。調(diào)用此函數(shù)并以當(dāng)前隊列為目標(biāo)會導(dǎo)致死鎖。
當(dāng)障礙塊到達(dá)專用并發(fā)隊列的前面時议惰,它不會立即執(zhí)行慎颗。相反,隊列等待,直到其當(dāng)前正在執(zhí)行的塊完成執(zhí)行俯萎。此時傲宜,隊列自己執(zhí)行屏障塊。在屏障塊完成之后夫啊,在屏障塊之后提交的任何塊都不會執(zhí)行函卒。
您指定的隊列應(yīng)該是您使用dispatch_queue_create函數(shù)自己創(chuàng)建的并發(fā)隊列。如果傳遞給此函數(shù)的隊列是串行隊列或全局并發(fā)隊列之一撇眯,則此函數(shù)的行為類似于dispatch_sync函數(shù)报嵌。
與dispatch_barrier_async不同,不對目標(biāo)隊列執(zhí)行保留熊榛。因為對此函數(shù)的調(diào)用是同步的锚国,所以它“借用”調(diào)用者的引用。此外玄坦,不對塊執(zhí)行Block_copy血筑。
作為優(yōu)化,此函數(shù)在可能的情況下調(diào)用當(dāng)前線程上的屏障塊煎楣。
關(guān)于單例模式的兩篇文章鏈接:
GCD6之dispatch_once及單例模式
dispatch_once
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
- 參數(shù)predicate介紹
predicate:
A pointer to a dispatch_once_t structure that is used to test whether the block has completed or not.
謂詞:
指向dispatch_once_t結(jié)構(gòu)的指針豺总,用于測試塊是否已完成。
- 簡介
Executes a block object once and only once for the lifetime of an application.
在應(yīng)用程序的生命周期內(nèi)執(zhí)行一次且僅執(zhí)行一次塊對象择懂。
該方法能夠保證在應(yīng)用的生命周期內(nèi)只執(zhí)行一次提交的任務(wù)喻喳,所以常用于單例類的創(chuàng)建。
- 一般創(chuàng)建:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行一次的任務(wù)
...
});
- 一個單例類的創(chuàng)建:
@interface MyUtil: NSObject <NSCopying,NSMutbaleCopying>
+ (instancetype)sharedInstance;
@end
@implementation MyUtil
static MyUtil *staticMyUtil = nil;
+ (instancetype)sharedInstance
{
//保證初始化創(chuàng)建只執(zhí)行一次困曙,注意是super
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
staticMyUtil = [[super allocWithZone:nil] init];
});
return staticMyUtil;
}
//防止通過alloc或new直接創(chuàng)建對象
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
return [self sharedInstance]
}
//實現(xiàn)NSCopying協(xié)議的方法表伦,防止通過copy獲取副本對象
- (instancetype)copyWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
//實現(xiàn)NSCopying協(xié)議的方法,防止通過copy獲取副本對象
- (instancetype)mutableCopyWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
@end
GCD7之dispatch_group
1. dispatch_group_t
typedef NSObject<OS_dispatch_group> *dispatch_group_t;
調(diào)度組赂弓,用于提交一組任務(wù)到組里。
A dispatch group is a mechanism for monitoring a set of blocks. Your application can monitor the blocks in the group synchronously or asynchronously depending on your needs. By extension, a group can be useful for synchronizing for code that depends on the completion of other tasks.
Note that the blocks in a group may be run on different queues, and each individual block can add more blocks to the group.
The dispatch group keeps track of how many blocks are outstanding, and GCD retains the group until all its associated blocks complete execution.
調(diào)度組是用于監(jiān)視一組塊的機(jī)制哪轿。 您的應(yīng)用程序可以根據(jù)您的需要同步或異步監(jiān)視組中的塊盈魁。 通過擴(kuò)展,一個組可用于同步依賴于其他任務(wù)完成的代碼窃诉。
請注意杨耙,組中的塊可以在不同的隊列上運行,并且每個單獨的塊可以向該組添加更多塊飘痛。
調(diào)度組會跟蹤未完成的塊數(shù)珊膜,GCD會保留該組,直到其所有關(guān)聯(lián)的塊完成執(zhí)行宣脉。
2. dispatch_group_create
用于創(chuàng)建dispatch_group_t調(diào)度組
dispatch_group_t dispatch_group_create(void);
3. dispatch_group_async
void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
Submits a block to a dispatch queue and associates the block with the specified dispatch group.The dispatch group can be used to wait for the completion of the block objects it references.
將任務(wù)異步提交到調(diào)度隊列车柠,并將任務(wù)與指定的調(diào)度組關(guān)聯(lián)。調(diào)度組可用于等待它引用的塊對象的完成。
等待group的任務(wù)都執(zhí)行完畢竹祷,可以調(diào)用下面兩個方法谈跛。
4. dispatch_group_notify
void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);
Schedules a block object to be submitted to a queue when a group of previously submitted block objects have completed.
在一組先前提交的任務(wù)完成時,將任務(wù)提交到指定隊列塑陵。
5. dispatch_group_wait
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
和dispatch_group_notify功能類似(多了一個dispatch_time_t參數(shù)可以設(shè)置超時時間)感憾,在group上任務(wù)完成前,dispatch_group_wait會阻塞當(dāng)前線程(所以不能放在主線程調(diào)用)一直等待令花;當(dāng)group上任務(wù)完成阻桅,或者等待時間超過設(shè)置的超時時間會結(jié)束等待;
簡單說來兼都,多了一個定時器嫂沉,會阻塞當(dāng)前線程,直到組內(nèi)任務(wù)完成俯抖,或者定時器到了输瓜,然后將任務(wù)提交給指定隊列。
6. 補(bǔ)充
dispatch_group會等和它關(guān)聯(lián)的所有的dispatch_queue_t上的任務(wù)都執(zhí)行完畢才會發(fā)出同步信號(dispathc_group_notify的代碼塊block會被執(zhí)行,group_wati會結(jié)束等待)芬萍。也就是說一個group可以關(guān)聯(lián)多個任務(wù)隊列尤揣。
這點和dispatch_barrier不一樣,dispatch_barrier只對同一個隊列上的有用柬祠。
7. 代碼示例
- (void)groupSync
{
dispatch_queue_t disqueue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t disgroup = dispatch_group_create();
dispatch_group_async(disgroup, disqueue, ^{
NSLog(@"任務(wù)一完成");
});
dispatch_group_async(disgroup, disqueue, ^{
sleep(8);
NSLog(@"任務(wù)二完成");
});
dispatch_group_notify(disgroup, disqueue, ^{
NSLog(@"dispatch_group_notify 執(zhí)行");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_wait(disgroup, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
NSLog(@"dispatch_group_wait 結(jié)束");
});
}
輸出:
2018-08-18 16:12:24.053425+0800 GCD[1568:268349] 任務(wù)一完成
2018-08-18 16:12:29.054188+0800 GCD[1568:268347] dispatch_group_wait 結(jié)束
2018-08-18 16:12:32.057110+0800 GCD[1568:268348] 任務(wù)二完成
2018-08-18 16:12:32.057352+0800 GCD[1568:268348] dispatch_group_notify 執(zhí)行
分析:任務(wù)1輸出北戏,任務(wù)二睡8秒,wait睡5秒漫蛔,wait輸出嗜愈,任務(wù)2醒來后輸出,notify輸出
關(guān)于dispatch_group_enter和dispatch_group_leave的介紹及使用莽龟,看這個鏈接
應(yīng)用場景
iOS中多個網(wǎng)絡(luò)請求的同步問題總結(jié)
信號量這篇總結(jié)的不太好蠕嫁,可以去找一下別的文章再看一下
GCD8之dispatch_semaphore(信號量)
dispatch_semaphore_create
dispatch_semaphore_t dispatch_semaphore_create(long value);
傳入的參數(shù)為long,輸出一個dispatch_semaphore_t類型且值為value的信號量毯盈,long必須大于等于0剃毒。
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
這個函數(shù)會使傳入的信號量dsema的值加1
dispatch_semaphore_wait
注意,正常的使用順序是先降低然后再提高搂赋,dispatch_semaphore_signal和dispatch_semaphore_wait這兩個函數(shù)通常成對使用赘阀。
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
如果dsema信號量的值大于0,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語句脑奠,并且將信號量的值減1基公;
如果desema的值為0,那么這個函數(shù)就阻塞當(dāng)前線程等待timeout(注意timeout的類型為dispatch_time_t宋欺,不能直接傳入整形或float型數(shù))轰豆,其所處線程自動執(zhí)行其后語句
dispatch_time_t
在設(shè)置timeout時胰伍,比較有用的兩個宏:DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER。
DISPATCH_TIME_NOW 表示當(dāng)前秒咨;
DISPATCH_TIME_FOREVER 表示遙遠(yuǎn)的未來喇辽;
一般可以直接設(shè)置timeout為這兩個宏其中的一個,或者自己創(chuàng)建一個dispatch_time_t類型的變量雨席。
創(chuàng)建dispatch_time_t類型的變量有兩種方法菩咨,dispatch_time和dispatch_walltime。
利用創(chuàng)建dispatch_time創(chuàng)建dispatch_time_t類型變量的時候一般也會用到這兩個變量陡厘。
dispatch_time的聲明如下:
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta)抽米;
其參數(shù)when需傳入一個dispatch_time_t類型的變量,和一個delta值糙置。表示when加delta時間就是timeout的時間云茸。
例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);
表示當(dāng)前時間向后延時一秒為timeout的時間。
(6)關(guān)于信號量谤饭,一般可以用停車來比喻标捺。
停車場剩余4個車位,那么即使同時來了四輛車也能停的下揉抵。如果此時來了五輛車亡容,那么就有一輛需要等待。
信號量的值就相當(dāng)于剩余車位的數(shù)目冤今,dispatch_semaphore_wait函數(shù)就相當(dāng)于來了一輛車闺兢,dispatch_semaphore_signal
就相當(dāng)于走了一輛車。停車位的剩余數(shù)目在初始化的時候就已經(jīng)指明了(dispatch_semaphore_create(long value))戏罢,
調(diào)用一次dispatch_semaphore_signal屋谭,剩余的車位就增加一個;調(diào)用一次dispatch_semaphore_wait剩余車位就減少一個龟糕;
當(dāng)剩余車位為0時桐磁,再來車(即調(diào)用dispatch_semaphore_wait)就只能等待。有可能同時有幾輛車等待一個停車位讲岁。有些車主
沒有耐心我擂,給自己設(shè)定了一段等待時間,這段時間內(nèi)等不到停車位就走了催首,如果等到了就開進(jìn)去停車扶踊。而有些車主就像把車停在這泄鹏,
所以就一直等下去郎任。
真實應(yīng)用:信號量dispatch_semaphore的用法
GCD9之dispatch_set_context、dispatch_get_context和dispatch_set_finalizer_f
dispatch_set_context可以為隊列添加上下文數(shù)據(jù)备籽,但是因為GCD是C語言接口形式的舶治,所以其context參數(shù)類型是“void *”分井。
需使用下面abc三種方式創(chuàng)建context,并且一般結(jié)合dispatch_set_finalizer_f使用霉猛,回收context內(nèi)存尺锚。
// dispatch_set_context、dispatch_get_context是為了向隊列中傳遞上下文context服務(wù)的惜浅。
// dispatch_set_finalizer_f相當(dāng)于dispatch_object_t的析構(gòu)函數(shù)瘫辩。
// 因為context的數(shù)據(jù)不是foundation對象,所以arc不會自動回收坛悉,一般在dispatch_set_finalizer_f中手動回收伐厌,所以一般講上述三個方法綁定使用。
- (void)test
{
// 幾種創(chuàng)建context的方式
// a裸影、用C語言的malloc創(chuàng)建context數(shù)據(jù)挣轨。
// b、用C++的new創(chuàng)建類對象轩猩。
// c卷扮、用Objective-C的對象,但是要用__bridge等關(guān)鍵字轉(zhuǎn)為Core Foundation對象均践。
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
if (queue) {
// "123"即為傳入的context
dispatch_set_context(queue, "123");
dispatch_set_finalizer_f(queue, &xigou);
}
dispatch_async(queue, ^{
char *string = dispatch_get_context(queue);
NSLog(@"%s", string);
});
}
// 該函數(shù)會在dispatch_object_t銷毀時調(diào)用晤锹。
void xigou(void *context)
{
// 釋放context的內(nèi)存(對應(yīng)上述abc)
// a、CFRelease(context);
// b浊猾、free(context);
// c抖甘、delete context;
}