在軟件開發(fā)中使用多線程可以大大地提高用戶體驗,提高效率代虾。Grand Central Dispatch(CGD)則是C語言的一套多線程開發(fā)框架进肯,相比NSThread和NSOperation,GCD更加高效棉磨,并且線程由系統(tǒng)管理江掩,會自動運行多核運算。因為這些優(yōu)勢乘瓤,GCD是Apple推薦給開發(fā)者使用的首選多線程解決方案环形。
1、GCD的調(diào)度機制
GCD框架中一個很重要的概念是調(diào)度隊列衙傀,我們對線程的操作實際上是由調(diào)度隊列完成的抬吟。我們只需要將要執(zhí)行的任務(wù)添加到合適的隊列中即可。在GCD框架中统抬,有如下三種類型的調(diào)度隊列火本。
1.1主隊列
其中的任務(wù)在主線程中執(zhí)行,因為其會阻塞主線程聪建,所以是一個串行的隊列钙畔。可以通過下面的方法得到:
dispatch_get_main_queue();
1.2全局并行隊列
隊列中任務(wù)的執(zhí)行嚴格按照先進先出的模式進行金麸。如果是串行的隊列擎析,則當(dāng)一個任務(wù)結(jié)束后,才會開啟另一個任務(wù)挥下,如果是并行隊列揍魂,則任務(wù)的開啟順序和添加順序是一致的。系統(tǒng)為iOS應(yīng)用自動創(chuàng)建了4個全局共享的并發(fā)隊列棚瘟。使用下面的函數(shù)獲得:
dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>);
上面函數(shù)的第一個參數(shù)是這個隊列的ID现斋,系統(tǒng)的4個全局隊列默認的優(yōu)先級不同,這個參數(shù)可填寫的定義如下:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 //優(yōu)先級別最高的全局隊列
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0//優(yōu)先級別中等的全局隊列
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//優(yōu)先級別較低的全局隊列
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后臺的全局隊列偎蘸,優(yōu)先級別最低
這個函數(shù)的第二個參數(shù)是一個預(yù)留參數(shù)庄蹋,我們可以傳NULL.
1.3自定義隊列
上面的兩種隊列都是系統(tǒng)為我們創(chuàng)建好的,我們只需要獲取到他們禀苦,添加任務(wù)即可蔓肯。當(dāng)然我們也可以創(chuàng)建自己的隊列遂鹊,包含串行和并行的振乏。使用如下方法來創(chuàng)建:
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
其中第一個參數(shù)是這個隊列的名字,第二個參數(shù)決定創(chuàng)建的是串行還是并行隊列秉扑。填寫DISPATCH_QUEUE_SERIAL或NULL創(chuàng)建串行隊列慧邮,填寫DISPATCH_QUEUE_CONCURRENT創(chuàng)建并行隊列调限。
2、添加任務(wù)到調(diào)度隊列中
使用dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)函數(shù)或者dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)函數(shù)來同步或異步的執(zhí)行任務(wù)误澳。示例如下:
- (void)creatGCDQueue {
//創(chuàng)建一個串行的隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
//向隊列中添加同步任務(wù)1
dispatch_sync(queue, ^{
NSLog(@"%@:task1",[NSThread currentThread]);
});
//向隊列中添加異步任務(wù)2
dispatch_async(queue, ^{
NSLog(@"%@:task2",[NSThread currentThread]);
});
}
//打印信息:
上面的代碼創(chuàng)建了一個串行的自定義隊列耻矮,并且向隊列中添加了一個同步的任務(wù)和一個異步的任務(wù)。需要注意忆谓,這里的同步和異步指的是針對當(dāng)前代碼運行所在的線程而言的裆装。
從打印信息可以看出,同步的任務(wù)是在主線程中執(zhí)行倡缠,異步的任務(wù)是在單獨的線程中執(zhí)行哨免,由于我們創(chuàng)建的調(diào)度隊列是串行的,因此先開啟了任務(wù)1昙沦,后開啟了任務(wù)2.
只有當(dāng)調(diào)度隊列是并行琢唾,而且向隊列中添加的任務(wù)也是異步的時候,多任務(wù)才會實現(xiàn)并行異步執(zhí)行盾饮。
實現(xiàn)如下:
- (void)creatGCDQueue {
//創(chuàng)建一個并行的隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
//向隊列中添加異步任務(wù)1
dispatch_async(queue, ^{
for (int i = 0; i < 15; i ++) {
NSLog(@"%@ = %d:task1",[NSThread currentThread],i);
}
});
//向隊列中添加異步任務(wù)2
dispatch_async(queue, ^{
for (int i = 0; i < 15; i ++) {
NSLog(@"%@ = %d:task2",[NSThread currentThread],i);
} });
}
3采桃、使用隊列組
通過前面的學(xué)習(xí),我們現(xiàn)在已經(jīng)可以運用隊列多線程執(zhí)行任務(wù)了丘损,但是GCD的強大之處遠遠不止如此普办。看下面的例子号俐。
如果有3個任務(wù)A泌豆、B、C吏饿,其中A與B是沒有關(guān)系的踪危,他們可以并行執(zhí)行,C必須是A猪落、B都結(jié)束之后才能執(zhí)行贞远,當(dāng)然,實現(xiàn)這樣的邏輯并不困難笨忌,使用KVO就可以實現(xiàn)蓝仲,但是如果使用隊列處理這樣的邏輯,則代碼會更加清晰簡單官疲。
可以使用dispatch_group_create()創(chuàng)建一個隊列組袱结,使用如下函數(shù)將隊列添加到隊列組中:
void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
隊列中的隊列是異步執(zhí)行的,示例如下:
- (void)creatGCDGroup {
//創(chuàng)建一個隊列組
dispatch_group_t group = dispatch_group_create();
//創(chuàng)建一個異步隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
//添加任務(wù)
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 10; i ++) {
NSLog(@"%@ = %d:task1",[NSThread currentThread],i);
}
});
//添加任務(wù)
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 10; i ++) {
NSLog(@"%@ = %d:task2",[NSThread currentThread],i);
}
});
//阻塞線程途凫,直到前面的隊列任務(wù)執(zhí)行完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
for (int i = 0; i < 10; i ++) {
NSLog(@"%@ = %d:over",[NSThread currentThread],i);
}
}
打印結(jié)果如下:
以上代碼完美的實現(xiàn)了我們的任務(wù)依賴需求垢夹,可以看出GCD的強大了吧,復(fù)雜的任務(wù)邏輯關(guān)系因為GCD變得十分清晰簡單维费。
4果元、GCD對循環(huán)任務(wù)的處理
說到循環(huán)促王,除了常規(guī)的while循環(huán),for循環(huán)外而晒,for-in也是開發(fā)中常用的一種循環(huán)方式蝇狼。for-in循環(huán)通常來進行數(shù)組或字典的遍歷,這種遍歷通常不關(guān)心循環(huán)執(zhí)行的順序倡怎。使用GCD迅耘,配合設(shè)備的多核運算技術(shù),我們可以將這種循環(huán)遍歷的性能提升到極致监署,示例如下:
- (void)creatGCDApply {
dispatch_apply(20, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
NSLog(@"%@:%zu",[NSThread currentThread],i);
});
}
打印信息如下:
從打印信息可以看出豹障,循環(huán)是由多個不同的線程完成的,比如我們的設(shè)備是8核的CPU焦匈。因此每個線程單獨在一個核執(zhí)行血公,這將循環(huán)的運行效率提升到了極致。大大提高了運行速率缓熟。
5累魔、GCD中的消息與信號
5.1Dispatch Source
在GCD框架中提供了dispatch_source_t類型的對象,dispatch_source_t類型的對象可以用來傳遞和接收某個消息够滑。在任一線程上調(diào)用它的一個函數(shù) dispatch_source_merge_data 后垦写,會執(zhí)行 Dispatch Source 事先定義好的句柄(可以把句柄簡單理解為一個 block )。
這個過程叫 Custom event 彰触,用戶事件梯投。是 dispatch source 支持處理的一種事件。簡單地說况毅,這種事件是由你調(diào)用 dispatch_source_merge_data 函數(shù)來向自己發(fā)出的信號分蓖。
示例如下:
- (void)creatGCDSource {
//創(chuàng)建一個數(shù)據(jù)對象,DISPATCH_SOURCE_TYPE_DATA_ADD的含義表示當(dāng)數(shù)據(jù)變化時相加
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
//設(shè)置響應(yīng)分派源事件的block尔许,在分派源指定的隊列上運行
dispatch_source_set_event_handler(source, ^{
NSLog(@"%lu:sec",dispatch_source_get_data(source));//得到分派源的數(shù)據(jù)
dispatch_async(dispatch_get_main_queue(), ^{
//更新UI
});
});
//啟動
dispatch_resume(source);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//網(wǎng)絡(luò)請求
//向分派源發(fā)送事件么鹤,需要注意的是,不可以傳遞0值(事件不會被觸發(fā))味廊,同樣也不可以傳遞負數(shù)蒸甜。
dispatch_source_merge_data(source, 1);
});
}
注意:DISPATCH_SOURCE_TYPE_DATA_ADD是將所有觸發(fā)結(jié)果相加,最后統(tǒng)一執(zhí)行響應(yīng)余佛,但是加入sleepForTimeInterval后柠新,如果interval的時間越長,則每次觸發(fā)都會響應(yīng)辉巡,但是如果interval的時間很短恨憎,則會將觸發(fā)后的結(jié)果相加后統(tǒng)一觸發(fā)。這在更新UI時很有用红氯,比如更新進度條時框咙,沒必要每次觸發(fā)都響應(yīng),因為更新時還有其他的用戶操作(用戶輸入痢甘,觸碰等)喇嘱,所以可以統(tǒng)一觸發(fā)
比如我們寫一個進度條的示例:
- (void)creatGCDSource {
//1、指定DISPATCH_SOURCE_TYPE_DATA_ADD塞栅,做成Dispatch Source(分派源)者铜。設(shè)定Main Dispatch Queue 為追加處理的Dispatch Queue
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
__block NSUInteger totalComplete = 0;
dispatch_source_set_event_handler(source, ^{
//當(dāng)處理事件被最終執(zhí)行時,計算后的數(shù)據(jù)可以通過dispatch_source_get_data來獲取放椰。這個數(shù)據(jù)的值在每次響應(yīng)事件執(zhí)行后會被重置作烟,所以totalComplete的值是最終累積的值。
NSUInteger value = dispatch_source_get_data(source);
totalComplete += value;
NSLog(@"進度:%@", @((CGFloat)totalComplete/100));
NSLog(@":large_blue_circle:線程號:%@", [NSThread currentThread]);
});
//分派源創(chuàng)建時默認處于暫停狀態(tài)砾医,在分派源分派處理程序之前必須先恢復(fù)拿撩。
dispatch_resume(source);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、恢復(fù)源后如蚜,就可以通過dispatch_source_merge_data向Dispatch Source(分派源)發(fā)送事件:
dispatch_async(queue, ^{
for (NSUInteger index = 0; index < 100; index++) {
dispatch_source_merge_data(source, 1);
NSLog(@":recycle:線程號:%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
sleep(0.1);
}
});
}
5.2压恒、信號量 singer
信號量是GCD中一個很重要的概念,他的用法與消息的傳遞有所類似错邦,其本示例代碼如下:
- (void)creatGCDSinger {
//創(chuàng)建一個信號探赫,其中的參數(shù)是信號的初始值
dispatch_semaphore_t singer = dispatch_semaphore_create(0);
//發(fā)送信號,信號量+1
dispatch_semaphore_signal(singer);
//等待信號撬呢,當(dāng)信號量大于0時伦吠,執(zhí)行后面的代碼,否則等待魂拦,第二個參數(shù)為等待的超時時長毛仪,下面設(shè)置的為一直等待
dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
NSLog(@"singer");
}
注意,dispatch_semaphore_wait函數(shù)會阻塞當(dāng)前線程芯勘,在主線程中要慎用潭千。通過發(fā)送信號函數(shù):dispatch_semaphore_signal(),可以使信號量+1借尿,每次執(zhí)行過等待信號后刨晴,信號量會-1,如此路翻,我們可以很方便地控制不同隊列中方法的執(zhí)行流程狈癞。
5.2.1限制線程的最大并發(fā)數(shù)
- (void)creatGCDSinger {
//創(chuàng)建一個信號,其中的參數(shù)是信號的初始值
dispatch_semaphore_t singer = dispatch_semaphore_create(2);
for (int i = 0; i < 15; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//等待信號茂契,當(dāng)信號量大于0時蝶桶,執(zhí)行后面的代碼,否則等待掉冶,第二個參數(shù)為等待的超時時長真竖,下面設(shè)置的為一直等待
dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
//doing
sleep(1);
//發(fā)送信號脐雪,信號量+1
dispatch_semaphore_signal(singer);
});
}
}
如上述代碼可知,總共異步執(zhí)行15個任務(wù)恢共,但是由于我們設(shè)置了值為2的信號量战秋,每一次執(zhí)行任務(wù)的時候信號量都會先-1,而在任務(wù)結(jié)束后使信號量加1讨韭,當(dāng)信號量減到0的時候脂信,說明正在執(zhí)行的任務(wù)有2個,這個時候其它任務(wù)就會阻塞透硝,直到有任務(wù)被完成時狰闪,這些任務(wù)才會執(zhí)行。
注意濒生,信號量的正常的使用順序是先降低(dispatch_semaphore_wait)然后再提高(dispatch_semaphore_signal)埋泵,這兩個函數(shù)通常成對使用。
5.2.2阻塞發(fā)請求的線程
有些時候罪治,我們需要阻塞發(fā)送請求的線程秋泄,比如在多個請求回調(diào)后統(tǒng)一操作的需求,而這些請求之間并沒有順序關(guān)系规阀,且這些接口都會另開線程進行網(wǎng)絡(luò)請求的恒序。一般地,這種多線程完成后進行統(tǒng)一操作的需求都會使用隊列組(dispatch_group_t)來完成谁撼,但是由于是異步請求歧胁,沒等其異步回調(diào)之后,請求的線程就結(jié)束了厉碟,為此喊巍,就需要使用信號量來阻塞住發(fā)請求的線程。實現(xiàn)代碼如下:
- (void)creatGCDSinger {
//創(chuàng)建線程組
dispatch_group_t group = dispatch_group_create();
//獲取隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)1
dispatch_group_async(group, queue, ^{
//請求1
[self request1];
});
//任務(wù)2
dispatch_group_async(group, queue, ^{
//請求2
[self request2];
});
//任務(wù)3
dispatch_group_async(group, queue, ^{
//請求3
[self request3];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"-------所有網(wǎng)絡(luò)請求已請求完成-------");
});
}
- (void)request1 {
//創(chuàng)建信號量箍鼓,并設(shè)置為0崭参,信號量本質(zhì)是資源數(shù),為0表示用完款咖,需要等待
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//模擬網(wǎng)絡(luò)請求-異步
//每次網(wǎng)絡(luò)請求成功或失敗后何暮,都讓信號量+1,表示釋放當(dāng)前資源铐殃,其他線程可以搶占了
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功海洼,發(fā)送信號
dispatch_semaphore_signal(sema);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗,發(fā)送信號
dispatch_semaphore_signal(sema);
}];
//如果信號量為0富腊,表示沒有資源可用坏逢,便一直等待,不再往下執(zhí)行.只有當(dāng)網(wǎng)絡(luò)請求成功或失敗時,才會往下走
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
- (void)request2 {
//創(chuàng)建信號量是整,并設(shè)置為0肖揣,信號量本質(zhì)是資源數(shù),為0表示用完浮入,需要等待
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//模擬網(wǎng)絡(luò)請求-異步
//每次網(wǎng)絡(luò)請求成功或失敗后龙优,都讓信號量+1,表示釋放當(dāng)前資源舵盈,其他線程可以搶占了
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功,發(fā)送信號
dispatch_semaphore_signal(sema);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗球化,發(fā)送信號
dispatch_semaphore_signal(sema);
}];
//如果信號量為0秽晚,表示沒有資源可用,便一直等待筒愚,不再往下執(zhí)行
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
- (void)request3 {
//創(chuàng)建信號量赴蝇,并設(shè)置為0,信號量本質(zhì)是資源數(shù)巢掺,為0表示用完句伶,需要等待
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//模擬網(wǎng)絡(luò)請求-異步
//每次網(wǎng)絡(luò)請求成功或失敗后,都讓信號量+1陆淀,表示釋放當(dāng)前資源考余,其他線程可以搶占了
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功,發(fā)送信號
dispatch_semaphore_signal(sema);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗轧苫,發(fā)送信號
dispatch_semaphore_signal(sema);
}];
//如果信號量為0楚堤,表示沒有資源可用,便一直等待含懊,不再往下執(zhí)行
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
當(dāng)然身冬,我們也可以使用dispatch_group_enter和dispatch_group_leave來實現(xiàn)同樣的功能:
- (void)creatGCDSinger {
//創(chuàng)建線程組
dispatch_group_t group = dispatch_group_create();
//創(chuàng)建一個并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("group.queue", DISPATCH_QUEUE_CONCURRENT);
//任務(wù)1
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//請求1
[self request1WithGroup:group];
});
//任務(wù)2
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
//請求2
[self request2WithGroup:group];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"-------所有網(wǎng)絡(luò)請求已請求完成-------");
});
}
- (void)request1WithGroup:(dispatch_group_t)group {
//模擬網(wǎng)絡(luò)請求-異步
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功,調(diào)用level
dispatch_group_leave(group);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗岔乔,調(diào)用level
dispatch_group_leave(group);
}];
}
- (void)request2WithGroup:(dispatch_group_t)group
//模擬網(wǎng)絡(luò)請求-異步
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功酥筝,調(diào)用level
dispatch_group_leave(group);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗,調(diào)用level
dispatch_group_leave(group);
}];
}
5.2.3信號量控制網(wǎng)絡(luò)請求順序
- (void)creatGCDSinger {
//創(chuàng)建semp
dispatch_semaphore_t semp = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)1
dispatch_async(queue, ^{
//信號量-1
dispatch_semaphore_wait(semp, DISPATCH_TIME_FOREVER);
//模擬網(wǎng)絡(luò)請求
//模擬網(wǎng)絡(luò)請求-異步
//每次網(wǎng)絡(luò)請求成功或失敗后雏门,都讓信號量+1嘿歌,表示釋放當(dāng)前資源,其他線程可以搶占了
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功茁影,發(fā)送信號
dispatch_semaphore_signal(sema);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗搅幅,發(fā)送信號
dispatch_semaphore_signal(sema);
}];
});
//任務(wù)2
dispatch_async(queue, ^{
//信號量-1
dispatch_semaphore_wait(semp, DISPATCH_TIME_FOREVER);
//模擬網(wǎng)絡(luò)請求
//模擬網(wǎng)絡(luò)請求-異步
//每次網(wǎng)絡(luò)請求成功或失敗后,都讓信號量+1呼胚,表示釋放當(dāng)前資源茄唐,其他線程可以搶占了
[[KNetRequestManager share] getSomeData:^{
//網(wǎng)絡(luò)請求成功,發(fā)送信號
dispatch_semaphore_signal(sema);
} errorBlock:^{
//網(wǎng)絡(luò)請求失敗,發(fā)送信號
dispatch_semaphore_signal(sema);
}];
});
}
6沪编、隊列的掛起和開啟
在GCD框架中還提供了暫停與開始任務(wù)隊列的方法呼盆,使用下面的函數(shù)可以將隊列或隊列組暫時掛起和開啟:
//掛起隊列或隊列組
void dispatch_suspend(dispatch_object_t object);
//開啟隊列或隊列組
void dispatch_resume(dispatch_object_t object);
注意:在暫停隊列時,隊列中正在執(zhí)行的任務(wù)并不會中斷蚁廓,未開啟的任務(wù)會被掛起访圃。
7、數(shù)據(jù)存儲的線程安全問題-多度單寫
在進行多線程編程時相嵌,或許總會遇到這一類問題:數(shù)據(jù)的競爭與線程的安全腿时。這些問題如果通過程序手動來控制,則難度將會非常大饭宾。CGD同樣為我們簡單地解決了這樣的問題批糟。
首先,如果只是在讀取數(shù)據(jù)看铆,而不對數(shù)據(jù)做任何修改徽鼎,那么我們并不需要處理安全問題,可以讓多個任務(wù)同時進行讀取弹惦》裼伲可是如果要對數(shù)據(jù)進行寫操作,那么在同一時間棠隐,我們就必須只能有一個任務(wù)在寫石抡,CGD中有一個方法幫我們完美地解決了這個問題,示例如下:
- (void)creatCGDReadAndWriter {
//創(chuàng)建一個隊列
dispatch_queue_t queue = dispatch_queue_create("oneQueue", DISPATCH_QUEUE_CONCURRENT);
//多個任務(wù)同時執(zhí)行讀操作
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"read1:%d",i);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"read2:%d",i);
}
});
//執(zhí)行寫操作
/*
下面這個函數(shù)在加入隊列時不會執(zhí)行助泽,會等待已經(jīng)開始的異步執(zhí)行全部完成后再執(zhí)行汁雷,并且在執(zhí)行時會阻塞其他任務(wù)
當(dāng)執(zhí)行完成后,其他任務(wù)重新進入異步執(zhí)行
*/
dispatch_barrier_async(queue, ^{
for (int i = 0; i < 5; i ++) {
NSLog(@"writer:%d",i);
}
});
//績效執(zhí)行異步操作
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"read3:%d",i);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"read4:%d",i);
}
});
}
打印信息:
從打印信息可以看出讀操作是異步進行的报咳,寫操作是等待當(dāng)前任務(wù)結(jié)束后阻塞任務(wù)隊列獨立進行的侠讯,當(dāng)寫操作結(jié)束后隊列恢復(fù)異步執(zhí)行讀操作,這正是我們需要的效果暑刃。