多線程----GCD(牛逼的中樞調(diào)度器)

GCD的基本使用

需要經(jīng)常使用的兩個(gè)函數(shù)和隊(duì)列

  • 同步函數(shù)dispatch_sync:立刻馬上執(zhí)行,如果沒(méi)有執(zhí)行完畢,后面的也不會(huì)執(zhí)行
  • 異步函數(shù)dispatch_async:如果沒(méi)有執(zhí)行完畢,后面的也可以執(zhí)行
  • 并發(fā)隊(duì)列DISPATCH_QUEUE_CONCURRENT
  • 串行隊(duì)列DISPATCH_QUEUE_SERIAL

創(chuàng)建隊(duì)列方法

  • 方法1:自己創(chuàng)建一個(gè)新的線程
/**
       第一個(gè)參數(shù): C語(yǔ)言的字符串,標(biāo)簽
       第二個(gè)參數(shù): 隊(duì)列的類型
            DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
            DISPATCH_QUEUE_SERIAL:串行隊(duì)列
     */
    dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
  • 方法二:獲得全局并發(fā)隊(duì)列 且并不是一個(gè)任務(wù)一條線程,是系統(tǒng)隨機(jī)給你分配線程
// 獲得全局并發(fā)隊(duì)列:并不是一個(gè)任務(wù)一條線程,而是系統(tǒng)給你分配的線程
    /**
     第一個(gè)參數(shù): 優(yōu)先級(jí)
     第二個(gè)參數(shù): 未來(lái)使用的,默認(rèn) 0
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

封裝任務(wù)方法

 /**
     第一個(gè)參數(shù): 隊(duì)列
     第二個(gè)參數(shù): 要執(zhí)行的任務(wù)
     */
  • 方法一:異步函數(shù)
 dispatch_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)
  • 方法二:同步函數(shù)
dispatch_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

異步函數(shù)+并發(fā)隊(duì)列

會(huì)開(kāi)啟多條線程,隊(duì)列中的任務(wù)是并發(fā)(異步)執(zhí)行 無(wú)順序

// 異步函數(shù)+并發(fā)隊(duì)列:會(huì)開(kāi)啟多條線程,隊(duì)列中的任務(wù)是并發(fā)(異步)執(zhí)行無(wú)順序
- (void) asyncConcurrent{
    
    // 1.創(chuàng)建隊(duì)列
    /**
       第一個(gè)參數(shù): C語(yǔ)言的字符串,標(biāo)簽
       第二個(gè)參數(shù): 隊(duì)列的類型
            DISPATCH_QUEUE_CONCURRENT:并發(fā)隊(duì)列
            DISPATCH_QUEUE_SERIAL:串行隊(duì)列
     */
//    dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
    
    
    // 獲得全局并發(fā)隊(duì)列:并不是一個(gè)任務(wù)一條線程,而是系統(tǒng)給你分配的線程
    /**
     第一個(gè)參數(shù): 優(yōu)先級(jí)
     第二個(gè)參數(shù): 未來(lái)使用的,默認(rèn) 0
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2 .封裝任務(wù) -> 添加任務(wù)到隊(duì)列中
    /**
     第一個(gè)參數(shù): 隊(duì)列
     第二個(gè)參數(shù): 要執(zhí)行的任務(wù)
     */
    dispatch_async(queue, ^{
        NSLog(@"asyncConcurrent1------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncConcurrent2------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncConcurrent3------%@",[NSThread currentThread]);
    });

}

異步?函數(shù)+串行隊(duì)列

只會(huì)開(kāi)啟一條線程,任務(wù)是串行執(zhí)行有順序

// 異步?函數(shù)+串行隊(duì)列:只會(huì)開(kāi)啟一條線程,任務(wù)是串行執(zhí)行有順序
- (void) asyncSerial{
    //1. 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("asyncSerial", DISPATCH_QUEUE_SERIAL);
    
    // 2. 封裝任務(wù)
    dispatch_async(queue, ^{
        NSLog(@"asyncSerial1------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncSerial2------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncSerial3------%@",[NSThread currentThread]);
    });
}

同步函數(shù)+并發(fā)隊(duì)列

不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行

// 同步函數(shù)+并發(fā)隊(duì)列:不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
- (void) syncConcurrent{
    
    // 1. 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("syncConcurrent", DISPATCH_QUEUE_CONCURRENT);
    
    // 2. 封裝任務(wù)
    dispatch_sync(queue, ^{
        NSLog(@"syncConcurrent1------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncConcurrent2------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncConcurrent3------%@",[NSThread currentThread]);
    });
    
}

同步函數(shù)+串行隊(duì)列

不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行

// 同步函數(shù)+串行隊(duì)列:不會(huì)開(kāi)啟線程,任務(wù)是串行執(zhí)行
- (void) syncSerial{
    
    // 1. 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);
    
    // 2. 封裝任務(wù)
    dispatch_sync(queue, ^{
        NSLog(@"syncSerial1------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncSerial2------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncSerial3------%@",[NSThread currentThread]);
    });
}

特別的隊(duì)列----主隊(duì)列

特點(diǎn):如果主隊(duì)列中發(fā)現(xiàn)當(dāng)前主線程有任務(wù)在執(zhí)行,那么主隊(duì)列會(huì)暫停調(diào)用隊(duì)列中的任務(wù),直到主線程空閑為止

異步函數(shù)+主隊(duì)列

所有任務(wù)都在主線程中執(zhí)行,不會(huì)開(kāi)啟線程

- (void) asyncMain{
    
    // 1. 獲得主隊(duì)列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.封裝任務(wù) - 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"asyncMain1------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncMain2------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"asyncMain3------%@",[NSThread currentThread]);
    });
}
同步函數(shù)+主隊(duì)列

死鎖 如果該方法在子線程中執(zhí)行,那么所有的任務(wù)都會(huì)在主線程中執(zhí)行

// 同步函數(shù)+主隊(duì)列:死鎖
// 注意:如果該方法在子線程中執(zhí)行,那么所有的任務(wù)都會(huì)在主線程中執(zhí)行
- (void) syncMain{
    
    // 1. 獲得主隊(duì)列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.封裝任務(wù) - 同步函數(shù)
    dispatch_sync(queue, ^{
        NSLog(@"syncMain1------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncMain2------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"syncMain3------%@",[NSThread currentThread]);
    });
}

備注

dispatch_asyncdispatch_async_f的區(qū)別在于它們封裝任務(wù)的方法,dispatch_async使用的是block而dispatch_async_f使用的則是C語(yǔ)言函數(shù),其它是沒(méi)有任何區(qū)別的,鑒于_f用得比較少 這里只做簡(jiǎn)單的了解

- (void) note{
    
    // 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 區(qū)別:封裝任務(wù)的方法(block  函數(shù))
    
    int context = 10;
    /**
     * 第一個(gè)參數(shù):隊(duì)列
       第二個(gè)參數(shù):參數(shù) 函數(shù)指針 注意傳地址 不要傳值
       第三個(gè)參數(shù):要調(diào)用函數(shù)名稱 C語(yǔ)言函數(shù)
     */
    dispatch_async_f(queue, &context, task);
}

void task (void * context){
    int * c = context;
    NSLog(@"%d",*c);
}

GCD線程間的通信

這里以下載網(wǎng)絡(luò)圖片為例子

- (void) downloadImage{
    // 創(chuàng)建子線程下載圖片
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        // 1. 確定圖片url
        NSURL * url = [NSURL URLWithString:@"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1909600733,2304577724&fm=26&gp=0.jpg"];
        
        // 2.下載圖片二進(jìn)制數(shù)據(jù)到本地
        NSData * imageData = [NSData dataWithContentsOfURL:url];
        
        // 3.轉(zhuǎn)換圖片
        UIImage * image = [UIImage imageWithData:imageData];
        
        NSLog(@"當(dāng)前線程------%@",[NSThread currentThread]);
        
        // 更新UI 回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"當(dāng)前線程------%@",[NSThread currentThread]);
        });
        
    });
}

GCD的常用函數(shù)

延遲執(zhí)行

dispatch_after GCD 可以自由選擇在哪個(gè)線程執(zhí)行

// 方法3. GCD 可以自由選擇在哪個(gè)線程執(zhí)行
//    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    /**
     * 第一個(gè)參數(shù):DISPATCH_TIME_NOW 從現(xiàn)在開(kāi)始計(jì)算時(shí)間
       第一個(gè)參數(shù):延遲2.0 GCD時(shí)間單位:納秒  (需要 * 10的9次方 轉(zhuǎn)換為秒)
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"GCD延遲執(zhí)行------%@",[NSThread currentThread]);
        NSLog(@"------GCDend------");

    });

一次性代碼

dispatch_once 注意:不能夠在懶加載中使用

// 一次性代碼
- (void) once{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------noce------");
    });
}

柵欄函數(shù)

dispatch_barrier_async 當(dāng)之前的任務(wù)執(zhí)行完成之后才會(huì)執(zhí)行后面的任務(wù) 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列

// 1 獲取全局并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
    
    // 1. 異步函數(shù)
    dispatch_async(queue, ^{
        NSLog(@"download1------%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download3------%@",[NSThread currentThread]);
    });
    
    // 柵欄函數(shù)
    // 柵欄函數(shù)不能使用全局并發(fā)隊(duì)列
    dispatch_barrier_async(queue, ^{
        NSLog(@"+++++++++++++++++++++");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download2------%@",[NSThread currentThread]);
    });

GCD的快速迭代

dispatch_apply 因?yàn)樵诘倪^(guò)程中會(huì)開(kāi)啟子線程 所以比一般的for循環(huán)迭代要快速很多

// GCD快速迭代 :開(kāi)啟
- (void) apply{
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    /**
     * 第一個(gè)參數(shù):遍歷次數(shù)
       第二個(gè)參數(shù):隊(duì)列(并發(fā)隊(duì)列)
       第三個(gè)參數(shù):索引
     */
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zd-----------%@",index,[NSThread currentThread]);
    });
}

下面通過(guò)一個(gè)文件剪切的Demo來(lái)演示??

- (void) moveFileWithGCD{
    // 1. 拿到文件路徑
    NSString * form = @"/Users/ChangRJey/Desktop/修改前后對(duì)比圖";
    
    // 2. 獲得目標(biāo)文件路徑
    NSString * to = @"/Users/ChangRJey/Desktop/yoyoyo";
    
    // 3. 得到目錄下面的所有文件
    NSArray * subPaths=  [[NSFileManager defaultManager] subpathsAtPath:form];
    
    NSLog(@"文件名------%@",subPaths);
    
    // 4. 遍歷所有文件,然后執(zhí)行剪切操作
    NSInteger count = subPaths.count;
    dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSString * fullPath = [form stringByAppendingPathComponent:subPaths[index]];
        NSString * toFullPath = [to stringByAppendingPathComponent:subPaths[index]];
        
        NSLog(@"文件名------%@",fullPath);
        
        // 4.2 執(zhí)行剪切操作
        /**
         * 第一個(gè)參數(shù):要剪切的文件路徑
         第二個(gè)參數(shù):文件應(yīng)該被存放的目標(biāo)路徑
         第三個(gè)參數(shù):錯(cuò)誤信息
         */
        [[NSFileManager defaultManager] moveItemAtPath:fullPath toPath:toFullPath error:nil];
        NSLog(@"%@------%@------%@",fullPath,toFullPath,[NSThread currentThread]);
    });
}

GCD隊(duì)列組的基本使用

創(chuàng)建Group

dispatch_group_t

完成通知

dispatch_group_notify
  • 方法一 簡(jiǎn)化版
// 攔截通知,當(dāng)隊(duì)列組中所有任務(wù)都執(zhí)行完成完畢之后會(huì)進(jìn)行通知
// 內(nèi)部本身是異步的
- (void) groupOne{
    // 1. 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 2. 創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    
    // 3. 異步函數(shù)封裝任務(wù)
    dispatch_group_async(group, queue, ^{
        NSLog(@"download_group1------%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"download_group2------%@",[NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"download_group3------%@",[NSThread currentThread]);
    });
    
    // 攔截通知,當(dāng)隊(duì)列組中所有任務(wù)都執(zhí)行完成完畢之后會(huì)進(jìn)行通知
    // 內(nèi)部本身是異步的
    dispatch_group_notify(group, queue, ^{
        NSLog(@"------dispatch_group_notify------");
    });
}
  • 方法二 以前版本
- (void) groupTwo{
    // 1. 創(chuàng)建隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 2. 創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    
    // 3. 在該方法后面的異步任務(wù)會(huì)被納入到隊(duì)列組的監(jiān)聽(tīng)范圍
    // dispatch_group_enter | dispatch_group_leave 必須配對(duì)使用
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        NSLog(@"download_group1------%@",[NSThread currentThread]);
        
        // 離開(kāi)隊(duì)列組
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);

    dispatch_async(queue, ^{
        NSLog(@"download_group2------%@",[NSThread currentThread]);
        
        // 離開(kāi)隊(duì)列組
        dispatch_group_leave(group);
    });
    
//    dispatch_group_notify(group, queue, ^{
//        NSLog(@"------dispatch_group_notify------");
//    });
    
    // 等待
    // DISPATCH_TIME_FOREVER:死等,知道隊(duì)列組中所有任務(wù)執(zhí)行完畢之后才能執(zhí)行 本身是阻塞的
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"------end------");
    
}
示例

下面通過(guò)一個(gè)例子來(lái)實(shí)際使用GCD的隊(duì)列組 通過(guò)下載 圖片1 和 圖片2 之后然后合并兩張圖片 因?yàn)楹喜蓮垐D片必須得讓子線程中的 圖片1 和 圖片2 下載完成之后才能夠進(jìn)行合并圖片

- (void) groupThree{
    
    /**
     * 1. 下載圖片1 開(kāi)子線程
       2. 下載圖片2 開(kāi)子線程
       3. 合成圖片并顯示圖片 開(kāi)子線程
     */
    
    // 獲得并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 創(chuàng)建隊(duì)列組
    dispatch_group_t group = dispatch_group_create();
    
    // 1. 下載圖片1 開(kāi)子線程
    dispatch_group_async(group, queue, ^{
        NSLog(@"download_image1------%@",[NSThread currentThread]);

        // 1. 1 確定url
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502282744935&di=aaefb03a83917b6b94da36fbf69fa080&imgtype=0&src=http%3A%2F%2Fpic.ilitu.com%2Fy3%2F1475_39900960921.jpg"];
        
        // 1.2 下載圖片二進(jìn)制數(shù)據(jù)到本地
        NSData * imageData = [NSData dataWithContentsOfURL:url];
        
        // 1.3 轉(zhuǎn)換圖片
        self.image1 = [UIImage imageWithData:imageData];
    });
    
    
    // 2. 下載圖片1 開(kāi)子線程
    dispatch_group_async(group, queue, ^{
        NSLog(@"download_image2------%@",[NSThread currentThread]);

        // 2. 1 確定url
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1502282773716&di=9b788f0dda94c259067c2826795caef4&imgtype=0&src=http%3A%2F%2Fi3.sinaimg.cn%2Fgm%2F2015%2F0422%2FU3932P115DT20150422122125.jpg"];
        
        // 2.2 下載圖片二進(jìn)制數(shù)據(jù)到本地
        NSData * imageData = [NSData dataWithContentsOfURL:url];
        
        // 2.3 轉(zhuǎn)換圖片
        self.image2 = [UIImage imageWithData:imageData];
        
    });
    
    // 3. 合成圖片并顯示圖片 開(kāi)子線程
    dispatch_group_notify(group, queue, ^{
        NSLog(@"合并圖片------%@",[NSThread currentThread]);

        // 3.1 創(chuàng)建圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake(300, 400));
        
        // 3.2 畫(huà)圖1
        [self.image1 drawInRect:CGRectMake(0, 0, 300, 200)];
        self.image1 = nil;
        
        // 3.3 畫(huà)圖2
        [self.image2 drawInRect:CGRectMake(0, 200, 300, 200)];
        self.image2 = nil;
        
        // 3.4 根據(jù)上下文得到一張圖片
        UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 3.5 關(guān)閉上下文
        UIGraphicsEndImageContext();
        
        // 3.6 得到圖片 回到主線程刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"更新UI------%@",[NSThread currentThread]);
        });
        
    });
}

以上就是GCD所有的基本常用方法功能,希望能夠幫助到大家,所有Demo我已經(jīng)上傳到GitHub,有喜歡的朋友記得給個(gè)Stare,謝謝!
另外分享一個(gè)覺(jué)得好用的已經(jīng)封裝好的GCD

我是Renjiee 我要做最騷的程序猿????????????

最后編輯于
?著作權(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)容

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