題外話
原本打算每周至少整理兩篇博客的,看來真的是高估自己了...
在此給自己立個(gè)FLAG,一周至少一篇,懇請(qǐng)監(jiān)督(大哭...)
言歸正傳
本篇博客主要講解GCD的線程同步應(yīng)用,對(duì)于什么是GCD的基礎(chǔ)問題,不太明白的還是建議大家去看看基礎(chǔ),強(qiáng)烈推薦iOS多線程--徹底學(xué)會(huì)多線程之『GCD』(寫的真的很棒哦)
線程同步
- 線程組group
- 信號(hào)量semaphore
- 柵欄barrier
一. 線程組group
可以使用dispatch_group_async函數(shù)將多個(gè)任務(wù)關(guān)聯(lián)到一個(gè)線程組dispatch_group和相應(yīng)的隊(duì)列queue中,dispatch_group會(huì)并發(fā)地同時(shí)執(zhí)行這些任務(wù)。
而且dispatch_group可以用來阻塞一個(gè)線程,直到dispatch_group關(guān)聯(lián)的所有的任務(wù)完成執(zhí)行。有時(shí)候必須等待任務(wù)完成的結(jié)果,然后才能繼續(xù)后面的處理。
dispatch_group同步應(yīng)用
首先執(zhí)行任務(wù)1、任務(wù)2、任務(wù)3,執(zhí)行完后,接著執(zhí)行任務(wù)0
/**
* 創(chuàng)建一個(gè)隊(duì)列
* 參數(shù)1:隊(duì)列名(C語言寫法)
* 參數(shù)2:隊(duì)列類型(串行/并行)
* DISPATCH_QUEUE_SERIAL串行
* DISPATCH_QUEUE_CONCURRENT 并行
*/
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建一個(gè)調(diào)度組group(線程組)
dispatch_group_t group = dispatch_group_create();
// 向線程組中添加多個(gè)任務(wù)
dispatch_group_async(group, queue, ^{
NSLog(@"mission--1");
});
NSLog(@"===aaa===");
dispatch_group_async(group, queue, ^{
NSLog(@"mission--2");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"dispatch_group_notify:mission--0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"mission--3");
});
運(yùn)行結(jié)果

分析
1.dispatch_group會(huì)等和它關(guān)聯(lián)的所有的dispatch_queue上的任務(wù)都執(zhí)行完畢才會(huì)發(fā)出同步信號(hào),即dispathc_group_notify的代碼塊block才會(huì)被執(zhí)行。
2.dispathc_group_notify的作用:在group中的其他操作全部完成后,再操作自己的內(nèi)容,所以我們會(huì)看到上面任務(wù)1、任務(wù)2、任務(wù)3執(zhí)行之后,才執(zhí)行任務(wù)0。
問題升級(jí)
如使用AFNetworking添加異步任務(wù)時(shí),上述方法顯然無效。
因?yàn)榫W(wǎng)絡(luò)請(qǐng)求需要時(shí)間,而線程的執(zhí)行并不會(huì)等待請(qǐng)求完成后才真正算作完成,而是只負(fù)責(zé)將請(qǐng)求發(fā)出去,線程就認(rèn)為自己的任務(wù)算完成了,當(dāng)三個(gè)請(qǐng)求都發(fā)送出去,就會(huì)執(zhí)行dispathc_group_notify中的內(nèi)容,但請(qǐng)求結(jié)果返回的時(shí)間是不一定的,也就導(dǎo)致界面都刷新了,請(qǐng)求才返回,這就是無效的。
因此需要使用dispatch_group_enter、dispatch_group_leave(需要成對(duì)出現(xiàn))
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--1");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--2");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--3");
dispatch_group_leave(group);
}];
// 獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_notify(group, mainQueue, ^{
NSLog(@"mission--0");
});
為此我們還需要簡單的寫一個(gè)block回調(diào)
.m文件中添加擴(kuò)展,寫入:
- (void)demoBlock:(void(^)(NSString *str))block;
實(shí)現(xiàn)方法:
- (void)demoBlock:(void(^)(NSString *str))block {
block(@"demoBlock");
}
運(yùn)行結(jié)果

分析
1.和dispatch_async相比,當(dāng)我們調(diào)用n次dispatch_group_enter后再調(diào)用n次dispatch_group_leave時(shí),dispatch_group_notify和dispatch_group_wait會(huì)收到同步信號(hào)(這里沒有給出dispatch_group_wait的具體應(yīng)用,需要了解的朋友可以自行查閱相關(guān)內(nèi)容)
2.應(yīng)用場景:處理異步任務(wù)的同步,當(dāng)異步任務(wù)開始前調(diào)用dispatch_group_enter,異步任務(wù)結(jié)束后調(diào)用dispatch_group_leave。
適用于后臺(tái)批量下載,結(jié)束后主線程統(tǒng)一刷新UI。
二. 信號(hào)量semaphore
首先,我們必須了解什么是信號(hào)量,在這里給大家簡單的介紹一下(小白可以去查一些相關(guān)資料)

以下是簡單的個(gè)人理解(偽代碼)
信號(hào)量
信號(hào)量 初始值 v = 3
a come
if v < 0 wait
else v = v - 1
// v = 2; state: a in
b come
if v < 0 wait
else v = v - 1
// v = 1; state: b in
c come
if v < 0 wait
else v = v - 1
// v = 0; state: c in
d come
if v < 0 wait
else v = v - 1
// state: d wait
e come
if v < 0 wait
else v = v - 1
// state: e wait
f come
if v < 0 wait
else v = v - 1
// state: f wait
b out // v = v + 1 = 1
if v < 0 wait
else v = v - 1
// v = 0; state: f in
...
方法
- dispatch_semaphore_create:創(chuàng)建一個(gè)信號(hào)量(semaphore)
- dispatch_semaphore_signal:信號(hào)通知,即讓信號(hào)量+1
- dispatch_semaphore_wait:等待,直到信號(hào)量大于0時(shí),即可操作,同時(shí)將信號(hào)量-1
還是上面的例子
/**
* 獲取全局隊(duì)列
* 參數(shù)1:選擇的是哪個(gè)優(yōu)先級(jí)的全局隊(duì)列
* 參數(shù)2:作為保留字段備用(一般為0)
*/
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 創(chuàng)建線程組
dispatch_group_t group = dispatch_group_create();
// 向線程組中添加任務(wù)
dispatch_group_async(group, globalQueue, ^{
// 創(chuàng)建一個(gè)信號(hào)量 初始值為0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--1");
// 執(zhí)行了block之后 將信號(hào)量+1
dispatch_semaphore_signal(semaphore);
}];
// 在成功執(zhí)行block之前,信號(hào)量必須等待
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
NSLog(@"===aaa===");
dispatch_group_async(group, globalQueue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--2");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, globalQueue, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self demoBlock:^(NSString *str) {
NSLog(@"mission--3");
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, globalQueue, ^{
NSLog(@"dispatch_group_notify:mission--0");
});
運(yùn)行結(jié)果

分析
1.在每個(gè)請(qǐng)求開始之前,我們創(chuàng)建一個(gè)信號(hào)量,初始為0,在請(qǐng)求操作之后,我們?cè)O(shè)一個(gè)dispatch_semaphore_wait,在請(qǐng)求到結(jié)果之后,再將信號(hào)量+1,即執(zhí)行dispatch_semaphore_signal。
2.這樣做的目的是保證在請(qǐng)求結(jié)果沒有返回之前,一直讓線程等待在那里,這樣一個(gè)線程的任務(wù)一直在等待,就不會(huì)算作完成,notify的內(nèi)容也就不會(huì)執(zhí)行了,直到每個(gè)請(qǐng)求的結(jié)果都返回了,線程任務(wù)才能夠結(jié)束,這時(shí)候notify也才能夠執(zhí)行。
三. 柵欄barrier
直接先上個(gè)例子:首先執(zhí)行任務(wù)1、任務(wù)2、任務(wù)3,執(zhí)行完后,接著執(zhí)行任務(wù)0,任務(wù)0執(zhí)行之后,再執(zhí)行任務(wù)4、任務(wù)5、任務(wù)6

分別使用dispatch_barrier_sync函數(shù)和dispatch_barrier_async函數(shù)來完成
dispatch_barrier_sync
dispatch_queue_t queue = dispatch_queue_create("mickyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"mission--1");
});
dispatch_async(queue, ^{
NSLog(@"mission--2");
});
dispatch_async(queue, ^{
NSLog(@"mission--3");
});
dispatch_barrier_sync(queue, ^{
for (int i = 0; i < 10000; i++) {
if (700 == i) {
NSLog(@"point1");
} else if (800 == i) {
NSLog(@"point2");
} else if (900 == i) {
NSLog(@"point3");
}
}
NSLog(@"barrier");
});
NSLog(@"===aaa===");
dispatch_async(queue, ^{
NSLog(@"mission--4");
});
NSLog(@"===bbb===");
dispatch_async(queue, ^{
NSLog(@"mission--5");
});
dispatch_async(queue, ^{
NSLog(@"mission--6");
});
運(yùn)行結(jié)果

dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create("mickyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"mission--1");
});
dispatch_async(queue, ^{
NSLog(@"mission--2");
});
dispatch_async(queue, ^{
NSLog(@"mission--3");
});
dispatch_barrier_async(queue, ^{
for (int i = 0; i < 10000; i++) {
if (700 == i) {
NSLog(@"point1");
} else if (800 == i) {
NSLog(@"point2");
} else if (900 == i) {
NSLog(@"point3");
}
}
NSLog(@"barrier");
});
NSLog(@"===aaa===");
dispatch_async(queue, ^{
NSLog(@"mission--4");
});
NSLog(@"===bbb===");
dispatch_async(queue, ^{
NSLog(@"mission--5");
});
dispatch_async(queue, ^{
NSLog(@"mission--6");
});
運(yùn)行結(jié)果


分析
根據(jù)打印結(jié)果可以看出
1.dispatch_barrier_sync/dispatch_barrier_async都是等待當(dāng)前隊(duì)列的之前插入的任務(wù)執(zhí)行完之后再執(zhí)行自己的任務(wù),執(zhí)行完自己的任務(wù)之后再執(zhí)行當(dāng)前隊(duì)列的之后插入的任務(wù)。
2.aaa、bbb的輸出位置完全不同
sync的時(shí)候,aaa、bbb的輸出位置都是在任務(wù)0結(jié)束之后;
=async的時(shí)候,aaa、bbb的輸出位置可以在任務(wù)0結(jié)束之前或者任務(wù)0正在執(zhí)行中,又或者任務(wù)0結(jié)束之后。
3.barrier(dispatch_barrier_sync/dispatch_barrier_async)只是阻塞同隊(duì)列中后面的操作而已,而dispatch_barrier_async不阻塞當(dāng)前的線程。
結(jié)論
共同點(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ù);
3.dispatch_barrier_async不阻塞當(dāng)前的線程,而dispatch_barrier_sync/dispatch_barrier_async只是阻塞同隊(duì)列中后面的任務(wù)。
最后
若是本文內(nèi)容有錯(cuò)誤的地方,還請(qǐng)各位路過的大神們指點(diǎn)指點(diǎn)~