iOS開(kāi)發(fā)經(jīng)驗(yàn)(7)-GCD

目錄(GCD):
  1. 關(guān)鍵詞
  2. 混淆點(diǎn)
  3. 場(chǎng)景應(yīng)用
  4. 總結(jié)
1. 關(guān)鍵詞
  • 線程概念: 獨(dú)立執(zhí)行的代碼段,一個(gè)線程同時(shí)間只能執(zhí)行一個(gè)任務(wù),反之多線程并發(fā)就可以在同一時(shí)間執(zhí)行多個(gè)任務(wù)。
    • 一個(gè)線程中的任務(wù)是串行執(zhí)行的;
    • 一個(gè)線程中執(zhí)行多個(gè)任務(wù),只能一個(gè)一個(gè)的按順序執(zhí)行;
    • 因此比較耗時(shí)的操作應(yīng)該放在“非主線程”.
  • 任務(wù):執(zhí)行的操作(Block里的代碼)
  • 同步異步:
    • 同步在當(dāng)前線程中執(zhí)行,任務(wù)會(huì)立即執(zhí)行,它會(huì)阻塞當(dāng)前線程并等待 Block 中的任務(wù)執(zhí)行完畢,然后當(dāng)前線程才會(huì)繼續(xù)運(yùn)行,就是在發(fā)出一個(gè)功能調(diào)用時(shí),在沒(méi)有得到結(jié)果之前,該調(diào)用就不繼續(xù)往下運(yùn)行(調(diào)用)。是一定不會(huì)開(kāi)新線程的,也就是必須一件一件事做,等前一件做完了才能做下一件事。(一個(gè)線程,當(dāng)前線程。);
    • 異步:可以開(kāi)新線程,可以在新線程內(nèi)執(zhí)行任務(wù),并不意味著一定就會(huì)開(kāi)新線程;任務(wù)不會(huì)立即執(zhí)行,當(dāng)前線程會(huì)直接往下執(zhí)行,它不會(huì)阻塞當(dāng)前線程。當(dāng)一個(gè)異步過(guò)程調(diào)用發(fā)出后,調(diào)用者不能立刻得到結(jié)果。實(shí)際處理這個(gè)調(diào)用的任務(wù)在完成后,通過(guò)狀態(tài)、通知和回調(diào)來(lái)通知調(diào)用者。(多個(gè)線程,開(kāi)辟出來(lái)的新線程。);
    • 同步(sync) 和 異步(async) 的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線程,直到 Block 中的任務(wù)執(zhí)行完畢。
  • 隊(duì)列
    • 串行隊(duì)列:放到串行隊(duì)列的任務(wù),GCD 會(huì) FIFO(先進(jìn)先出) 地取出來(lái)一個(gè),執(zhí)行一個(gè),然后取下一個(gè),這樣一個(gè)一個(gè)的執(zhí)行,隊(duì)列中的任務(wù)一個(gè)一個(gè)順序執(zhí)行。(一個(gè)任務(wù)執(zhí)行->等待返回結(jié)果->下一個(gè)任務(wù)執(zhí)行);
    • 并行隊(duì)列:放到并行隊(duì)列的任務(wù),GCD 也會(huì) FIFO的取出來(lái),但不同的是,它取出來(lái)一個(gè)就會(huì)放到別的線程,然后再取出來(lái)一個(gè)又放到另一個(gè)的線程。這樣由于取的動(dòng)作很快,忽略不計(jì),看起來(lái),所有的任務(wù)都是一起執(zhí)行的。不過(guò)需要注意,GCD 會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量,所以如果任務(wù)很多,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行。有高、默認(rèn)、低和后臺(tái)4個(gè)優(yōu)先級(jí)。并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效.
    • 注意兩個(gè)非常常用的特殊隊(duì)列:
      • 主隊(duì)列
UI 操作放在主隊(duì)列中執(zhí)行
跟主線程相關(guān)聯(lián)的隊(duì)列!
主隊(duì)列是 GCD 自帶的一種特殊的串行隊(duì)列,注意是串行
主隊(duì)列中的任務(wù)都會(huì)在主線程中執(zhí)行
獲取主隊(duì)列
* 全局并發(fā)隊(duì)列
一般情況下,并發(fā)任務(wù)都可以放在全局并發(fā)隊(duì)列中
獲取全局并發(fā)隊(duì)列
  • 創(chuàng)建隊(duì)列方法
    1.使用dispatch_queue_create函數(shù)創(chuàng)建隊(duì)列
串行隊(duì)列
//(隊(duì)列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("serial_queue", NULL);
并發(fā)隊(duì)列:
dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);

2.使用主隊(duì)列(跟主線程相關(guān)聯(lián)的隊(duì)列)
主隊(duì)列是GCD自帶的一種特殊的串行隊(duì)列:放在主隊(duì)列中的任務(wù),都會(huì)放到主線程中執(zhí)行??梢允褂?code>dispatch_get_main_queue()獲得系統(tǒng)提供的主隊(duì)列:

dispatch_queue_t queue = dispatch_get_main_queue();

3.使用dispatch_get_global_queue
獲得全局并發(fā)隊(duì)列。
GCD默認(rèn)已經(jīng)提供了全局的并發(fā)隊(duì)列,供整個(gè)應(yīng)用使用,可以無(wú)需手動(dòng)創(chuàng)建。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • Critical Section (臨界區(qū))
    簡(jiǎn)而言之就是兩個(gè)或多個(gè)線程不能同時(shí)執(zhí)行一段代碼去操作一個(gè)共享的資源
  • Race Condition (競(jìng)態(tài)條件)
    這種情況是指基于特定序列或時(shí)機(jī)的事件的軟件系統(tǒng)以不受控制的方式運(yùn)行的行為,
    競(jìng)態(tài)條件可導(dǎo)致無(wú)法預(yù)測(cè)的行為,例如程序的并發(fā)任務(wù)執(zhí)行的確切順序
  • Deadlock (死鎖)
    所謂的死鎖是指兩個(gè)(或多個(gè))線程都卡住了,都在等待對(duì)方完成后執(zhí)行,
    第一個(gè)不能完成是因?yàn)樵诘却诙€(gè)的完成,但第二個(gè)也不能完成,
    是因?yàn)樗诘却谝粋€(gè)完成
  • 多線程安全:當(dāng)某線程訪問(wèn)一個(gè)數(shù)據(jù)之前就要給數(shù)據(jù)加鎖,讓其不被其他的線程所修改。
    互斥鎖:
    互斥鎖的使用前提:多條線程搶奪同一塊資源的時(shí)候使用

@synchronized(鎖對(duì)象) {
// 需要鎖定的代碼
};
```
如果給數(shù)據(jù)加了鎖,就等于將這些異步的子線程變成同步的了,這也叫做線程同步技術(shù)。
互斥鎖的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問(wèn)題
缺點(diǎn):需要消耗大量的CPU資源

2. 混淆點(diǎn)
  • 隊(duì)列和線程區(qū)別:
    簡(jiǎn)單來(lái)說(shuō),隊(duì)列就是用來(lái)存放任務(wù)的“暫存區(qū)”,而線程是執(zhí)行任務(wù)的路徑,GCD將這些存在于隊(duì)列的任務(wù)取出來(lái)放到相應(yīng)的線程上去執(zhí)行,而隊(duì)列的性質(zhì)決定了在其中的任務(wù)在哪種線程上執(zhí)行。dispatch_asyndispatch_sync添加任務(wù)到dispatch隊(duì)列時(shí),是否創(chuàng)建線程呢,那么創(chuàng)建線程是創(chuàng)建一個(gè)呢還是多個(gè)呢?
    • dispatch_sync添加任務(wù)到隊(duì)列,不會(huì)創(chuàng)建新的線程,都是在當(dāng)前線程中處理的。無(wú)論添加到串行隊(duì)列里或者并行隊(duì)列里,都是串行效果,因?yàn)檫@個(gè)方法是等任務(wù)執(zhí)行完成以后才會(huì)返回。
    • dispatch_async添加任務(wù)到
      • mainQueue不創(chuàng)建線程,在主線程中串行執(zhí)行
      • globalQueue 和 并行隊(duì)列:根據(jù)任務(wù)系統(tǒng)決定開(kāi)辟線程個(gè)數(shù)
      • 串行對(duì)列:創(chuàng)建一個(gè)線程:串行執(zhí)行。
  • dispatch_sync()dispatch_async()的區(qū)別:
    • dispatch_async()函數(shù)是非同步的,它只負(fù)責(zé)將任務(wù)添加到隊(duì)列中,并不在乎添加到隊(duì)列中的任務(wù)是否處理完成。而相對(duì)于dispatch_async()
    • dispatch_sync()函數(shù)是同步的,dispatch_sync()函數(shù)不但負(fù)責(zé)將任務(wù)添加到隊(duì)列中,還要等待添加的任務(wù)執(zhí)行完成再返回,在此過(guò)程調(diào)用dispatch_sync()函數(shù)所在的線程被掛起,直到dispatch_sync()函數(shù)返回,所在線程恢復(fù),注意**是調(diào)用dispatch_sync()函數(shù)的線程被掛起。
  • 理解dispatch_syncdispatch_async 的工作流程
dispatch_sync(queue,block) 做了兩件事:
1)將block添加到queue隊(duì)列中
2)阻塞調(diào)用線程,等待block()執(zhí)行結(jié)束,回到調(diào)用線程。
dispatch_async(queue,block) 做了兩件事
1)將block添加到queue隊(duì)列;
2)直接回到調(diào)用線程(不阻塞調(diào)用線程)。
當(dāng)在main_thread中調(diào)用dispatch_sync 時(shí):
1)main_thread被阻塞,無(wú)法繼續(xù)執(zhí)行;
2)同步派發(fā)sync導(dǎo)致block()需要在main_thread中執(zhí)行結(jié)束才回返回;
3)而此時(shí)main_thread被阻塞,二者相互等待,死鎖。
  • 進(jìn)一步解釋死鎖
    函數(shù)比較重要的一個(gè)問(wèn)題就是死鎖,為什么會(huì)出現(xiàn)死鎖的情況呢?
    比如多我們有一個(gè)串行隊(duì)列,并且dispatch_sync()函數(shù)的調(diào)用也是在該隊(duì)列中,這樣串行隊(duì)列的線程在調(diào)用dispatch_sync()函數(shù)的時(shí)候被掛起,而線程被掛起之后dispatch_sync()函數(shù)添加的任務(wù)一直得不到線程的處理,一直不能返回,所以線程將一直處于被掛起的狀態(tài)。出現(xiàn)這種狀況的核心就是:調(diào)用dispatch_sync()函數(shù)的線程(注意是線程,而不是隊(duì)列,并行隊(duì)列有多個(gè)線程可能并不會(huì)發(fā)生這種狀況,除非調(diào)用函數(shù)的任務(wù)和函數(shù)追加的任務(wù)被分配到并行隊(duì)列中同一線程中去)和處理函數(shù)追加的任務(wù)的線程是同一個(gè)線程。此時(shí)就會(huì)發(fā)生死鎖。
    強(qiáng)調(diào)一點(diǎn),Dispatch Queue隊(duì)列并不是指我們印象中的線程隊(duì)列,它是任務(wù)隊(duì)列,它只負(fù)責(zé)任務(wù)的管理,并不進(jìn)行任務(wù)的執(zhí)行操作,任務(wù)的執(zhí)行是由Dispatch Queue分配的線程來(lái)完成的
    避免死鎖的方法是在使用dispatch_sync執(zhí)行任務(wù)時(shí),傳入?yún)?shù)的隊(duì)列不要和當(dāng)前線程的隊(duì)列是一樣的。

下面用代碼結(jié)合文字說(shuō)一下:
dispatch_sync和 dispatch_async需要兩個(gè)參數(shù),一個(gè)是隊(duì)列,一個(gè)是block,它們的共同點(diǎn)是block都會(huì)在你指定的隊(duì)列上執(zhí)行(無(wú)論隊(duì)列是并行隊(duì)列還是串行隊(duì)列),不同的是dispatch_sync會(huì)阻塞當(dāng)前調(diào)用GCD的線程直到block結(jié)束,而dispatch_async異步繼續(xù)執(zhí)行。例子如下面:

-(void)func{
dispatch_async(someQueue, ^{
//do some work.
NSLog(@"Here 1.");
});
NSLog(@"Here 2.");
}

因?yàn)閐ispatch_async異步非阻塞,所以Here 1.和Here 2.的打印順序不確定;

-(void)func{
dispatch_sync(someQueue, ^{
//do some work.
NSLog(@"Here 1.");
});
NSLog(@"Here 2.");
}

因?yàn)閐ispatch_sync阻塞當(dāng)前操作知道block返回,所以打印順序一定是Here 1. 然后再打印Here 2.

3. 場(chǎng)景應(yīng)用
  • 用 GCD 的快速迭代
第一個(gè)參數(shù): 迭代次數(shù),第二個(gè)參數(shù): 線程隊(duì)列(并發(fā)隊(duì)列) ,第三個(gè)參數(shù): index 索引
dispatch_apply(10000, dispatch_get_global_queue(0, 0), ^(size_t index) { 
NSLog(@"GCD- %zd -- %@", index, [NSThread currentThread]);
 }); 
  • GCD 計(jì)時(shí)器應(yīng)用
    NSTimer 的定時(shí)器是在 RunLoop 中實(shí)現(xiàn)的,由于RunLoop在處理各種任務(wù),所以會(huì)造成計(jì)時(shí)器不夠準(zhǔn)確,有時(shí)候會(huì)相對(duì)慢一些,有沒(méi)有什么方法會(huì)讓計(jì)時(shí)變得準(zhǔn)確?有,使用 GCD 的計(jì)時(shí)器方法會(huì)讓計(jì)時(shí)器變得相對(duì)準(zhǔn)確,而且GCD不受RunLoop的 Mode 影響。
    我們需要做的是,選擇其隊(duì)列類型,這里我選擇的是全局隊(duì)列。
dispatch Queue :決定了將來(lái)回調(diào)的方法在哪里執(zhí)行。
dispatch_source_t timer  是一個(gè)OC對(duì)象
DISPATCH_TIME_NOW  第二個(gè)參數(shù):定時(shí)器開(kāi)始時(shí)間,也可以使用如下的方法,在Now 的時(shí)間基礎(chǔ)上再延時(shí)多長(zhǎng)時(shí)間執(zhí)行以下任務(wù)。
 dispatch_time(<#dispatch_time_t when#>, <#int64_t delta#>)
intervalInSeconds  第三個(gè)參數(shù):定時(shí)器開(kāi)始后的間隔時(shí)間(納秒 NSEC_PER_SEC)
 leewayInSeconds 第四個(gè)參數(shù):間隔精準(zhǔn)度,0代標(biāo)最精準(zhǔn),傳入一個(gè)大于0的數(shù),代表多少秒的范圍是可以接收的,主要為了提高程序性能,積攢一定的時(shí)間,Runloop執(zhí)行完任務(wù)會(huì)睡覺(jué),這個(gè)方法讓他多睡一會(huì),積攢時(shí)間,任務(wù)也就相應(yīng)多了一點(diǎn),而后一起執(zhí)行
 // 全局隊(duì)列
dispatch_queue_t  queue = dispatch_get_global_queue(0, 0);
// 創(chuàng)建一個(gè) timer 類型定時(shí)器 ( DISPATCH_SOURCE_TYPE_TIMER)
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//設(shè)置定時(shí)器的各種屬性(何時(shí)開(kāi)始,間隔多久執(zhí)行)
// GCD 的時(shí)間參數(shù)一般為納秒 (1 秒 = 10 的 9 次方 納秒)
// 指定定時(shí)器開(kāi)始的時(shí)間和間隔的時(shí)間
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0);
// 任務(wù)回調(diào)
dispatch_source_set_event_handler(timer, ^{
    NSLog(@"-----定時(shí)器-------");
});
// 開(kāi)始定時(shí)器任務(wù)(定時(shí)器默認(rèn)開(kāi)始是暫停的,需要復(fù)位開(kāi)啟)
dispatch_resume(timer);
  • GCD實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)按鈕以例:
// 開(kāi)啟倒計(jì)時(shí)效果
(IBAction)openCountdown:(id)sender {
    __block NSInteger time = 59; //倒計(jì)時(shí)時(shí)間
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
 dispatch_source_set_timer(timer,DISPATCH_TIME_NOW,1.0*NSEC_PER_SEC, 0); //每秒執(zhí)行
    dispatch_source_set_event_handler(timer, ^{
        if(time <= 0){ //倒計(jì)時(shí)結(jié)束,關(guān)閉
            dispatch_source_cancel(timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                //設(shè)置按鈕的樣式
                [self.openSeconds setTitle:@"重新發(fā)送" forState:UIControlStateNormal];
                self.timeLabel.text = @"開(kāi)始";
                self.openSeconds.userInteractionEnabled = YES;
            });
        }else{
            int seconds = time % 60;
            dispatch_async(dispatch_get_main_queue(), ^{
                //設(shè)置label讀秒效果
                self.timeLabel.text = [NSString stringWithFormat:@"重新發(fā)送(%.2d)",seconds];
                [self.openSeconds setTitle:@"已發(fā)送" forState:UIControlStateNormal];
              // 在這個(gè)狀態(tài)下 用戶交互關(guān)閉,防止再次點(diǎn)擊 button 再次計(jì)時(shí)
                self.openSeconds.userInteractionEnabled = NO;
            });
            time--;
        }
    });
    dispatch_resume(timer);
}
  • 循環(huán)執(zhí)行任務(wù)
    dispatch_apply類似一個(gè)for循環(huán),并發(fā)的執(zhí)行每一項(xiàng)。所有任務(wù)結(jié)束后,dispatch_apply才會(huì)返回,會(huì)阻塞當(dāng)前線程。如果傳入隊(duì)列是串行隊(duì)列,要注意防止死鎖現(xiàn)象的發(fā)生。
循環(huán)執(zhí)行任務(wù),任務(wù)的順序是無(wú)序列的并且會(huì)堵塞當(dāng)前的線程。 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
count: 循環(huán)執(zhí)行次數(shù) 
queue: 隊(duì)列,可以是串行隊(duì)列或者是并行隊(duì)列 
block: 任務(wù) 
dispatch_apply(count, queue, ^(size_t i) {
 NSLog(@"%zu %@", i, [NSThread currentThread]); 
});
  • 延時(shí)執(zhí)行
    iOS常見(jiàn)的延時(shí)執(zhí)行有2種方式
    調(diào)用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再調(diào)用self的run方法

使用GCD函數(shù)延遲執(zhí)行:dispatch_after
不需要再寫(xiě)方法,且它還傳遞了一個(gè)隊(duì)列,我們可以指定并安排其線程。如果隊(duì)列是主隊(duì)列,那么就在主線程執(zhí)行,如果隊(duì)列是并發(fā)隊(duì)列,那么會(huì)新開(kāi)啟一個(gè)線程,在子線程中執(zhí)行。

延遲執(zhí)行, 這段代碼將會(huì)在2秒后將任務(wù)插入RunLoop當(dāng)中
dispatch_queue_t queue= dispatch_get_main_queue(); 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{ 
    NSLog(@"主隊(duì)列--延遲執(zhí)行------%@",[NSThread currentThread]); 
});
  • 子線程與主線程的通信:
    例子:從網(wǎng)絡(luò)加載圖片(在子線程),加載完成就更新UIView(在主線程)。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
              //加載圖片
              NSData *dataFromURL = [NSData dataWithContentsOfURL:imageURL];
              UIImage *imageFromData = [UIImage imageWithData:dataFromURL];
      dispatch_async(dispatch_get_main_queue(), ^{
              //加載完成更新view
              UIImageView *imageView = [[UIImageView alloc] initWithImage:imageFromData];          
      });      
  });
  • dispatch_once
    需求點(diǎn):用于在程序啟動(dòng)到終止,只執(zhí)行一次的代碼。此代碼被執(zhí)行后,相當(dāng)于自身全部被加上了注釋,不會(huì)再執(zhí)行了。為了實(shí)現(xiàn)這個(gè)需求,我們需要使用dispatch_once
    讓代碼在運(yùn)行一次后即刻被“雪藏”。
//使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過(guò)程中只被執(zhí)行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ 
// 只執(zhí)行1次的代碼,這里默認(rèn)是線程安全的:不會(huì)有其他線程可以訪問(wèn)到這里
});

單例模式:可以保證在程序運(yùn)行過(guò)程,一個(gè)類只有一個(gè)實(shí)例,而且該實(shí)例易于供外界訪問(wèn),從而方便地控制了實(shí)例個(gè)數(shù),并節(jié)約系統(tǒng)資源
使用場(chǎng)合
在整個(gè)應(yīng)用程序中,共享一份資源(這份資源只需要?jiǎng)?chuàng)建初始化1次)

static MBGlobalTool *_instance = nil;
 (instancetype)sharedInstance
{
  static dispatch_once_t onceToken ;
  dispatch_once(&onceToken, ^{
    _instance = [[self alloc] init] ;
  }) ;
  return _instance ;
}
  • 隊(duì)列組-dispatch_group
    需求點(diǎn):執(zhí)行多個(gè)耗時(shí)的異步任務(wù),但是只能等到這些任務(wù)都執(zhí)行完畢后,才能在主線程執(zhí)行某個(gè)任務(wù)。為了實(shí)現(xiàn)這個(gè)需求,我們需要讓將這些異步執(zhí)行的操作放在dispatch_group_async函數(shù)中執(zhí)行,最后再調(diào)用dispatch_group_notify來(lái)執(zhí)行最后執(zhí)行的任務(wù)。
    首先:分別異步執(zhí)行2個(gè)耗時(shí)的操作
    其次:等2個(gè)異步操作都執(zhí)行完畢后,再回到主線程執(zhí)行操作
    如果想要快速高效地實(shí)現(xiàn)上述需求,可以考慮用隊(duì)列組
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í)行完畢后,回到主線程...
});
  • 柵欄-dispatch_barrier
    需求點(diǎn):雖然我們有時(shí)要執(zhí)行幾個(gè)不同的異步任務(wù),但是我們還是要將其分成兩組:當(dāng)?shù)谝唤M異步任務(wù)都執(zhí)行完成后才執(zhí)行第二組的異步任務(wù)。這里的組可以包含一個(gè)任務(wù),也可以包含多個(gè)任務(wù)。
    為了實(shí)現(xiàn)這個(gè)需求,我們需要使用dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    在兩組任務(wù)之間形成“柵欄”,使其“下方”的異步任務(wù)在其“上方”的異步任務(wù)都完成之前是無(wú)法執(zhí)行的。
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT); 
dispatch_async(queue, ^{
 NSLog(@"----任務(wù) 1-----"); });
 dispatch_async(queue, ^{
 NSLog(@"----任務(wù) 2-----"); });
dispatch_barrier_async(queue, ^{
 NSLog(@"----barrier-----");
 });
dispatch_async(queue, ^{ 
NSLog(@"----任務(wù) 3-----"); 
}); 
dispatch_async(queue, ^{ 
NSLog(@"----任務(wù) 4-----"); });
4. 總結(jié)
  • 同步異步函數(shù)的作用:將任務(wù)添加到隊(duì)列中

  • 隊(duì)列作用:決定任務(wù)執(zhí)行的順序,先添加的先執(zhí)行,最后添加的任務(wù)最后執(zhí)行

  • 同步函數(shù):堵塞當(dāng)前線程;需要等待任務(wù)結(jié)束才能返回;在當(dāng)前線程中進(jìn)行;無(wú)開(kāi)辟線程的權(quán)限

  • 異步函數(shù):不會(huì)堵塞當(dāng)前線程;不需要等待任務(wù);有開(kāi)辟線程的權(quán)限

  • 隊(duì)列和任務(wù)關(guān)系;任務(wù)放在隊(duì)列里

  • 任務(wù)跟線程關(guān)系:任務(wù)需要線程來(lái)執(zhí)行;

  • 線程跟隊(duì)列關(guān)系:一個(gè)隊(duì)列里可能有好多線程,在一個(gè)線程內(nèi)可能也有多個(gè)隊(duì)列;在某個(gè)線程里創(chuàng)建隊(duì)列,那么這個(gè)隊(duì)列是屬于這個(gè)線程的;

  • 主隊(duì)列和主線程的關(guān)系:主隊(duì)列是主線中的一個(gè)串行隊(duì)列。所有的和UI的操作(刷新或者點(diǎn)擊按鈕)都必須在主線程中的主隊(duì)列中去執(zhí)行,否則無(wú)法更新UI,每一個(gè)應(yīng)用程序只有唯一的一個(gè)主隊(duì)列用來(lái)update UI。如果在主線里創(chuàng)建了一個(gè)自定義的隊(duì)列,那么這個(gè)隊(duì)列也就屬于主線程的隊(duì)列。

  • 注意重點(diǎn):果在主線里創(chuàng)建了一個(gè)自定義的隊(duì)列,且如果主線程在主隊(duì)列中被堵塞了,那么主線程會(huì)跑到這個(gè)自定義隊(duì)列里看看有沒(méi)有任務(wù),如果有就執(zhí)行。

  • 如果在主線程中創(chuàng)建自定義隊(duì)列(串行或者并行均可),在這個(gè)隊(duì)列中執(zhí)行同步任務(wù),同樣可以更新UI操作,主隊(duì)列中可以更新UI,自定義隊(duì)列也可以更新UI,但自定義隊(duì)列的更新UI的前提是在主線程中執(zhí)行同步任務(wù)

具體場(chǎng)景解釋:
同步主隊(duì)列死鎖原因:
在主線程里,開(kāi)啟同步任務(wù),并打算使用主隊(duì)列,當(dāng)開(kāi)始執(zhí)行同步函數(shù)的時(shí)候,這時(shí)候發(fā)生了什么:

  1. 主隊(duì)列會(huì)把同步函數(shù)任務(wù)放到主隊(duì)列的最前面也就是最先執(zhí)行的地方
  2. 同步函數(shù)堵塞當(dāng)前的線程也就是主線程,并把block中的任務(wù)添加到主隊(duì)列,這時(shí)候block中的任務(wù)跟同步任務(wù)都在同一個(gè)線程、同一個(gè)隊(duì)列,block中的任務(wù)排在同步任務(wù)的后面,等待前面任務(wù)(同步任務(wù))的執(zhí)行。
  3. 同步函數(shù)的特點(diǎn)是必須等待block中的任務(wù)執(zhí)行完才能返回,但是同步任務(wù)和block中的任務(wù)都在主隊(duì)列里,根據(jù)先進(jìn)先執(zhí)行(FIFO)的原則,會(huì)發(fā)生這樣的無(wú)限循環(huán)現(xiàn)象:同步任務(wù)等待block任務(wù)執(zhí)行完,但是同步任務(wù)又排在了block任務(wù)的前面,block任務(wù)不能執(zhí)行,那就等于同步任務(wù)也不能執(zhí)行完成,所以產(chǎn)生死鎖現(xiàn)象。

同步串行隊(duì)列不會(huì)發(fā)生死鎖原因:
在主線程里,開(kāi)啟同步任務(wù),并自定義串行隊(duì)列,當(dāng)開(kāi)始執(zhí)行同步函數(shù)的時(shí)候,這時(shí)候發(fā)生了什么:

  1. 主隊(duì)列會(huì)把同步函數(shù)任務(wù)放到主隊(duì)列的最前面也就是最先執(zhí)行的地方
  2. 同步函數(shù)堵塞當(dāng)前的線程也就是主線程,并把block中的任務(wù)添加到自定義串行隊(duì)列,這時(shí)候,在主線程也就有了兩個(gè)隊(duì)列,block中的任務(wù)跟同步任務(wù)都在同一個(gè)線程,但是不在同一個(gè)隊(duì)列。
  3. 主線程在主隊(duì)列被同步函數(shù)堵塞,會(huì)跑的它的其他隊(duì)列里,看看有沒(méi)有要執(zhí)行的任務(wù)。所以它會(huì)去自定義隊(duì)列里去執(zhí)行任務(wù)。
  4. 同步函數(shù)的特點(diǎn)是必須等待block中的任務(wù)執(zhí)行完才能返回,自定義隊(duì)列里的任務(wù)已被執(zhí)行完,同步函數(shù)也就執(zhí)行完返回主線程。

重點(diǎn):雖然主隊(duì)列中的主線程被堵塞了,但是主線程可以去執(zhí)行其他屬于主線程的隊(duì)列的任務(wù)。

最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 背景 擔(dān)心了兩周的我終于輪到去醫(yī)院做胃鏡檢查了!去的時(shí)候我都想好了最壞的可能(胃癌),之前在網(wǎng)上查的癥狀都很相似。...
    Dely閱讀 9,408評(píng)論 21 42
  • 本篇博客共分以下幾個(gè)模塊來(lái)介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點(diǎn)比較? GCD中的三種隊(duì)列...
    dullgrass閱讀 38,128評(píng)論 28 236
  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字?jǐn)?shù) 4996閱讀 20199...
    DanDanC閱讀 1,324評(píng)論 0 0
  • 簡(jiǎn)介 GCD(Grand Central Dispatch)是在macOS10.6提出來(lái)的,后來(lái)在iOS4.0被引...
    sunmumu1222閱讀 960評(píng)論 0 2
  • 從哪說(shuō)起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個(gè)最簡(jiǎn)單的問(wèn)題,以這個(gè)作為切入點(diǎn)好了 在ma...
    Mr_Baymax閱讀 2,917評(píng)論 1 17

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