iOS GCD線程同步

題外話
原本打算每周至少整理兩篇博客的,看來真的是高估自己了...
在此給自己立個(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é)果

dispatch_group_01.png

分析
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é)果

dispatch_group_02.png

分析
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)資料)


信號(hào)量.png

以下是簡單的個(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é)果

dispatch_semaphore.png

分析
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


barrier.png

分別使用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_sync.png

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é)果

dispatch_barrier_async_01.png

dispatch_barrier_async_02.png

分析
根據(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)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容