本文是GCD多線程編程基礎(chǔ)內(nèi)容的小結(jié),通過本文,你可以了解到:
- 多線程的幾個(gè)基本概念:進(jìn)程與線程、串行與并發(fā)
- GCD中的2個(gè)核心內(nèi)容:隊(duì)列、任務(wù)
- GCD的基本使用步驟
- GCD中使用同步異步方式添加任務(wù)到串行并發(fā)隊(duì)列后執(zhí)行的實(shí)際效果
- GCD中產(chǎn)生死鎖的原因以及實(shí)際開發(fā)中如何避免死鎖crash
GCD
Apple為了讓開發(fā)者更加容易的使用設(shè)備上的多核CPU,蘋果在 OS X 10.6 和 iOS 4 中引入了 Grand Central Dispatch(GCD),它是 Apple 開發(fā)的一個(gè)多核編程的較新的解決方法,它主要用于優(yōu)化應(yīng)用程序以支持多核處理器,它是一個(gè)在線程池模式的基礎(chǔ)上執(zhí)行的并發(fā)任務(wù),是我們平常開發(fā)中最常見的一種多線程編程方式
多線程基本概念
進(jìn)程與線程
對(duì)于操作系統(tǒng)來說,一個(gè)任務(wù)就是一個(gè)進(jìn)程(Process),比如打開一個(gè)瀏覽器就是啟動(dòng)一個(gè)瀏覽器進(jìn)程,打開一個(gè)記事本就啟動(dòng)了一個(gè)記事本進(jìn)程,打開兩個(gè)記事本就啟動(dòng)了兩個(gè)記事本進(jìn)程,打開一個(gè)Word就啟動(dòng)了一個(gè)Word進(jìn)程。
有些進(jìn)程還不止同時(shí)干一件事,比如Word,它可以同時(shí)進(jìn)行打字、拼寫檢查、打印等事情。在一個(gè)進(jìn)程內(nèi)部,要同時(shí)干多件事,就需要同時(shí)運(yùn)行多個(gè)“子任務(wù)”,我們把進(jìn)程內(nèi)的這些“子任務(wù)”稱為線程(Thread)。
由于每個(gè)進(jìn)程至少要干一件事,所以,一個(gè)進(jìn)程至少有一個(gè)線程。當(dāng)然,像Word這種復(fù)雜的進(jìn)程可以有多個(gè)線程,多個(gè)線程可以同時(shí)執(zhí)行,多線程的執(zhí)行方式和多進(jìn)程是一樣的,也是由操作系統(tǒng)在多個(gè)線程之間快速切換,讓每個(gè)線程都短暫地交替運(yùn)行,看起來就像同時(shí)執(zhí)行一樣。當(dāng)然,真正地同時(shí)執(zhí)行多線程需要多核CPU才可能實(shí)現(xiàn)。
串行和并發(fā)
并發(fā)就是多個(gè)任務(wù)在執(zhí)行的過程中,時(shí)間互相重疊,一個(gè)任務(wù)執(zhí)行沒結(jié)束,另一個(gè)已經(jīng)開始。
串行就是任務(wù)一個(gè)一個(gè)的執(zhí)行,時(shí)間上不相互重疊,一個(gè)任務(wù)執(zhí)行結(jié)束,下一個(gè)任務(wù)才能開始執(zhí)行。
GCG隊(duì)列
隊(duì)列是一種特殊的線性表,特殊之處在于它只允許在表的后端進(jìn)入插入操作,在表的前端進(jìn)行刪除操作,即遵循FIFO原則。
GCD中的隊(duì)列(Dispatch Queue)就是指用來執(zhí)行任務(wù)的等待隊(duì)列,當(dāng)我們添加任務(wù)到隊(duì)列之后,開發(fā)者不用再直接跟線程打交道了,只需要向隊(duì)列中添加代碼塊即可,GCD 在后端管理著一個(gè)線程池。GCD 不僅決定著你的代碼塊將在哪個(gè)線程被執(zhí)行,它還根據(jù)可用的系統(tǒng)資源對(duì)這些線程進(jìn)行管理。這樣可以將開發(fā)者從線程管理的工作中解放出來,通過集中的管理線程,來緩解大量線程被創(chuàng)建的問題。
GCD中的隊(duì)列可以分為以下2種:
-
串行隊(duì)列 ( Serial Dispatch Queue )
串行隊(duì)列(也稱為私有調(diào)度隊(duì)列)按照將他們添加到隊(duì)列順序一次執(zhí)行一個(gè)任務(wù)。當(dāng)前正在執(zhí)行的任務(wù)在由隊(duì)列管理的不同線程(可能因任務(wù)而異)上運(yùn)行。串行隊(duì)列通常用于同步對(duì)特定資源的訪問。
-
并發(fā)隊(duì)列 ( Concurrent Dispatch Queue )
并發(fā)隊(duì)列(也稱為一種全局調(diào)度隊(duì)列)同時(shí)執(zhí)行一個(gè)或多個(gè)任務(wù),但任務(wù)仍按其添加到隊(duì)列的順序啟動(dòng)。當(dāng)前正在執(zhí)行的任務(wù)在由調(diào)度隊(duì)列管理的不同線程上運(yùn)行。在任何給定點(diǎn)執(zhí)行的任務(wù)的確切數(shù)量是可變的,取決于系統(tǒng)條件。
在我們平時(shí)的開發(fā)中,還有2種我們最常見的,也是使用頻率最高的隊(duì)列:
-
主隊(duì)列 ( Main Dispatch Queue )
主隊(duì)列是一個(gè)全局可用的串行隊(duì)列,它在應(yīng)用程序的主線程上執(zhí)行任務(wù)。此隊(duì)列與應(yīng)用程序的
Runloop一起工作,將有序任務(wù)的執(zhí)行與附加到Runloop的其他事件源的執(zhí)行交錯(cuò)。因?yàn)樗趹?yīng)用程序的主線程上運(yùn)行,所以主隊(duì)列通常用作應(yīng)用程序的關(guān)鍵同步點(diǎn)。主隊(duì)列下的任務(wù)不管是異步任務(wù)還是同步任務(wù)都不會(huì)開辟線程,任務(wù)只會(huì)在主線程順序執(zhí)行
-
全局并發(fā)隊(duì)列 ( Global Dispatch Queue )
全局并發(fā)隊(duì)列本質(zhì)上是一個(gè)并發(fā)隊(duì)列,有系統(tǒng)提供,方便編程,可以不用創(chuàng)建就可以直接使用
GCD任務(wù)
任務(wù)就是你要在線程中執(zhí)行的代碼,在GCD中是用Block來定義任務(wù)的,是用起來非常靈活便捷。
GCD中執(zhí)行任務(wù)的方式有兩種:同步執(zhí)行(sync)與異步執(zhí)行(async)
-
同步執(zhí)行
同步執(zhí)行就是指使用
dispatch_sync方法將任務(wù)同步的添加到隊(duì)列里,在添加的任務(wù)執(zhí)行結(jié)束之前,當(dāng)前線程會(huì)被阻塞,然后會(huì)一直等待,直到任務(wù)完成。dispatch_sync添加的任務(wù)只能在當(dāng)前線程執(zhí)行,不具備開啟新線程的能力 -
異步執(zhí)行
異步執(zhí)行就是指使用
dispatch_async方法將任務(wù)異步的添加到隊(duì)列里,它不需要等待任務(wù)執(zhí)行結(jié)束,不需要做任何等待就能繼續(xù)執(zhí)行任務(wù)dispatch_async添加的任務(wù)可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力,但并不一定會(huì)開啟新線程
GCD的使用步驟
這個(gè)就跟趙本山跟宋丹丹的小品《鐘點(diǎn)工》里提出的把大象裝進(jìn)冰箱的經(jīng)典問題一樣,都是分三步:
把大象裝進(jìn)冰箱
- 把冰箱門打開
- 把大象裝進(jìn)去
- 把冰箱門關(guān)上
GCD使用步驟
- 創(chuàng)建或獲取一個(gè)隊(duì)列
- 定制需要執(zhí)行的任務(wù)
- 將任務(wù)追加到隊(duì)列
創(chuàng)建或獲取一個(gè)隊(duì)列
使用
dispatch_get_main_queue()獲取主隊(duì)列。-
使用
dispatch_get_global_queue獲取全局并發(fā)隊(duì)列,這個(gè)函數(shù)有2個(gè)參數(shù),第一個(gè)參數(shù)是全局隊(duì)列的優(yōu)先級(jí),一般情況下,使用的都是DISPATCH_QUEUE_PRIORITY_DEFAULT優(yōu)先級(jí),第二個(gè)參數(shù)是一個(gè)保留字段,我們需要給它一個(gè)0,否則這個(gè)函數(shù)會(huì)返回一個(gè)NULL,導(dǎo)致我們獲取不到正常的全局隊(duì)列。Reserved for future use. Passing any value other than zero may result in a NULL return value. -
使用
dispatch_queue_create函數(shù),創(chuàng)建自定義的串行或并行隊(duì)列,這個(gè)函數(shù)的定義如下:* @param label * A string label to attach to the queue. * This parameter is optional and may be NULL. * * @param attr * A predefined attribute such as DISPATCH_QUEUE_SERIAL, * DISPATCH_QUEUE_CONCURRENT, or the result of a call to * a dispatch_queue_attr_make_with_* function. * * @result * The newly created dispatch queue. */ API_AVAILABLE(macos(10.6), ios(4.0)) DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);dispatch_queue_create函數(shù)最后返回了一個(gè)隊(duì)列dispatch_queue_t,這個(gè)函數(shù)有2個(gè)參數(shù),第一個(gè)參數(shù)其實(shí)可以看成是是我們給這個(gè)隊(duì)列取的名字,以便后續(xù)debug,蘋果官方是推薦開發(fā)者使用逆序全程域名。第二個(gè)參數(shù),是用于確定這個(gè)隊(duì)列是串行隊(duì)列還是并發(fā)隊(duì)列,使用
DISPATCH_QUEUE_CONCURRENT表示創(chuàng)建的隊(duì)列是并發(fā)隊(duì)列,使用DISPATCH_QUEUE_SERIAL或者NULL表示創(chuàng)建的隊(duì)列是串行隊(duì)列,它們兩個(gè)其實(shí)是等價(jià)的,見下面的注釋:/*! * @const DISPATCH_QUEUE_SERIAL * * @discussion * An attribute that can be used to create a dispatch queue that invokes blocks * serially in FIFO order. * * See dispatch_queue_serial_t. */ #define DISPATCH_QUEUE_SERIAL NULL不過個(gè)人不推薦使用
NULL的方式來表示創(chuàng)建的是串行隊(duì)列,這種方式在多人開發(fā)時(shí),閱讀性是比較差的。以下是獲取或創(chuàng)建的4種方式:
//獲取自定義串行隊(duì)列 self.serialQueue = dispatch_queue_create("com.zed.customSerialQueue", DISPATCH_QUEUE_SERIAL); //獲取自定義并發(fā)隊(duì)列 self.concurrentQueue = dispatch_queue_create("com.zed.customConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); //獲取主隊(duì)列 dispatch_queue_t mainQueue = dispatch_get_main_queue(); //獲取全局并發(fā)隊(duì)列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
定制需要執(zhí)行的任務(wù)
GCD種的任務(wù)其實(shí)就是一個(gè)Block,就是我們俗稱的代碼塊,在這個(gè)代碼塊里面,把我們需要做的事情就是,將我們的任務(wù)代碼加入到這個(gè)block中
void (^block)(void) = ^{
NSLog(@"執(zhí)行任務(wù)");
for (int i = 0; i<100; i++) {
NSLog(@"%d",i);
}
NSLog(@"Thread:%@",[NSThread currentThread]);
};
將任務(wù)追加到隊(duì)列
GCD提供了2個(gè)方法用于將任務(wù)追加到隊(duì)列:
-
dispatch_sync使用同步執(zhí)行的方式追加到隊(duì)列 -
dispatch_async使用異步的方式追加到隊(duì)列
//三、將任務(wù)增加到隊(duì)列中
dispatch_async(globalQueue, block);
GCD的基本使用
前面我們已經(jīng)介紹了兩種基本隊(duì)列(串行隊(duì)列與并發(fā)隊(duì)列),兩種特殊隊(duì)列(主隊(duì)列與全局并發(fā)隊(duì)列),兩種任務(wù)執(zhí)行方式(同步執(zhí)行與異步執(zhí)行),所以,我們就有了8中不同的組合方式,不過由于全局并發(fā)隊(duì)列跟普通并發(fā)隊(duì)列的性質(zhì)是差不多的,所以,我們就有6中不同的組合,接下來,我們從3個(gè)角度來觀察這6種組合方式的效果:
三個(gè)角度
- 是否開啟線程
- 任務(wù)是按序執(zhí)行還是交替(同時(shí))執(zhí)行
- 是否阻塞當(dāng)前線程
六種組合方式
- 同步執(zhí)行+并發(fā)隊(duì)列
- 異步執(zhí)行+并發(fā)隊(duì)列
- 同步執(zhí)行+串行隊(duì)列
- 異步執(zhí)行+串行隊(duì)列
- 同步執(zhí)行+主隊(duì)列
- 異步執(zhí)行+主隊(duì)列
同步執(zhí)行+并發(fā)隊(duì)列
#pragma mark - 同步執(zhí)行+并發(fā)隊(duì)列
/*
* 特點(diǎn):
* 1.在當(dāng)前線程中執(zhí)行任務(wù),不會(huì)開啟新線程
* 2.按序執(zhí)行任務(wù),執(zhí)行行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
* 3.會(huì)阻塞當(dāng)前線程
*/
- (IBAction)executeSyncConcurrencyTask:(UIButton *)sender {
NSLog(@"CurrentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncConcurrencyTask---begin");
dispatch_sync(self.concurrentQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(self.concurrentQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(self.concurrentQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"SyncConcurrencyTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 13:42:07.265811+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] CurrentThread---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:07.265945+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] SyncConcurrencyTask---begin
2019-04-22 13:42:09.266681+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 1---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:11.268049+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 1---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:12.299360+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868133] XPC connection interrupted
2019-04-22 13:42:13.269544+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 2---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:15.270540+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 2---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:17.271936+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 3---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:19.273478+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] 3---<NSThread: 0x600002969400>{number = 1, name = main}
2019-04-22 13:42:19.273713+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] SyncConcurrencyTask---end
2019-04-22 13:42:19.273857+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[8668:1868026] *********************************************************
通過我們的代碼測(cè)驗(yàn),可以看出:
所有的任務(wù)都是在主線程(當(dāng)前線程)中執(zhí)行的,并沒有開啟新的線程,這也說明了同步執(zhí)行的一個(gè)特性:同步執(zhí)行任務(wù)不具備開啟新線程的能力。
任務(wù)1、任務(wù)2、任務(wù)3是按順序執(zhí)行的,并沒有出現(xiàn)并發(fā)執(zhí)行的情況,這是因?yàn)殡m然并發(fā)隊(duì)列具備同時(shí)執(zhí)行多個(gè)任務(wù)的能力,但是由于是同步執(zhí)行不具備開啟新線程的能力,所以,即使任務(wù)被追加到了并發(fā)隊(duì)列,它也沒有辦法去開啟新的線程,只能在當(dāng)前線程中執(zhí)行任務(wù)。
從我們的log中可以看出,我們所有的任務(wù)都是在
begin與end之間的,所以說,它會(huì)阻塞當(dāng)前線程,等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束,才會(huì)繼續(xù)執(zhí)行下面的代碼。
異步執(zhí)行+并發(fā)隊(duì)列
#pragma mark - 異步執(zhí)行+并發(fā)隊(duì)列
/*
* 特點(diǎn):
* 1.開啟多個(gè)新線程執(zhí)行任務(wù)
* 2.任務(wù)交替(同時(shí))執(zhí)行
* 3.不會(huì)阻塞當(dāng)前線程
*/
- (IBAction)executeAsyncConcurrencyTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncConcurrencyTask---begin");
dispatch_async(self.concurrentQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(self.concurrentQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(self.concurrentQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncConcurrencyTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 14:32:14.941222+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009244] CurrentThread begin---<NSThread: 0x6000000cea40>{number = 1, name = main}
2019-04-22 14:32:14.941405+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009244] AsyncConcurrencyTask---begin
2019-04-22 14:32:14.941608+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009244] CurrentThread end---<NSThread: 0x6000000cea40>{number = 1, name = main}
2019-04-22 14:32:14.941757+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009244] AsyncConcurrencyTask---end
2019-04-22 14:32:14.941894+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009244] *********************************************************
2019-04-22 14:32:16.945860+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009294] 1---<NSThread: 0x6000000a2380>{number = 4, name = (null)}
2019-04-22 14:32:16.945909+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2011461] 3---<NSThread: 0x6000000a2340>{number = 6, name = (null)}
2019-04-22 14:32:16.945909+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2011460] 2---<NSThread: 0x6000000aec00>{number = 5, name = (null)}
2019-04-22 14:32:18.951120+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2011460] 2---<NSThread: 0x6000000aec00>{number = 5, name = (null)}
2019-04-22 14:32:18.951121+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2011461] 3---<NSThread: 0x6000000a2340>{number = 6, name = (null)}
2019-04-22 14:32:18.951120+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9417:2009294] 1---<NSThread: 0x6000000a2380>{number = 4, name = (null)}
通過我們的代碼測(cè)驗(yàn),可以看出:
- 除了在主線程執(zhí)行的2個(gè)log任務(wù)之外,系統(tǒng)又開啟了3個(gè)線程用于執(zhí)行追加的三個(gè)任務(wù),說明異步執(zhí)行具備開啟新線程的能力,并且并發(fā)隊(duì)列可以開啟多個(gè)線程,交替執(zhí)行多個(gè)任務(wù)。
- 從我們的log中可以看到,
begin的log之后,馬上就是end的log,因此可以看出,它并不會(huì)阻塞當(dāng)前線程,并不需要等待追加的任務(wù)執(zhí)行完成。
同步執(zhí)行+串行隊(duì)列
#pragma mark - 同步執(zhí)行+串行隊(duì)列
/*
* 特點(diǎn):
* 1.在當(dāng)前線程中執(zhí)行任務(wù),不會(huì)開啟新線程
* 2.按序執(zhí)行任務(wù),執(zhí)行行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
* 3.會(huì)阻塞當(dāng)前線程
*/
- (IBAction)executeSyncSerialTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncSerialTask---begin");
dispatch_sync(self.serialQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(self.serialQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(self.serialQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncSerialTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 15:02:52.760352+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] CurrentThread begin---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:02:52.760558+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] SyncSerialTask---begin
2019-04-22 15:02:54.761971+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 1---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:02:56.762653+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 1---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:02:58.764202+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 2---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:03:00.765234+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 2---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:03:02.766464+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 3---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:03:04.767966+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] 3---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:03:04.768230+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] CurrentThread end---<NSThread: 0x600000222800>{number = 1, name = main}
2019-04-22 15:03:04.768379+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] SyncSerialTask---end
2019-04-22 15:03:04.768516+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[9826:2087150] *********************************************************
通過我們的代碼測(cè)驗(yàn),可以看出:
- 所有的任務(wù)都是在主線程(當(dāng)前線程)中執(zhí)行的,并且是順序執(zhí)行的,沒有開啟新的線程。
- 從我們的log中可以看出,我們所有的任務(wù)都是在
begin與end之間的,所以說,它會(huì)阻塞當(dāng)前線程,等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束,才會(huì)繼續(xù)執(zhí)行下面的代碼。
異步執(zhí)行+串行隊(duì)列
#pragma mark - 異步執(zhí)行+串行隊(duì)列
/*
* 特點(diǎn):
* 1.會(huì)開啟一條新線程
* 2.按序執(zhí)行任務(wù),執(zhí)行行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
* 3.不會(huì)阻塞當(dāng)前線程
*/
- (IBAction)executeAsyncSerialTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncSerialTask---begin");
dispatch_async(self.serialQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(self.serialQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(self.serialQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncSerialTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 15:25:00.103488+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154024] CurrentThread begin---<NSThread: 0x6000019c9400>{number = 1, name = main}
2019-04-22 15:25:00.103734+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154024] AsyncSerialTask---begin
2019-04-22 15:25:00.103888+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154024] CurrentThread end---<NSThread: 0x6000019c9400>{number = 1, name = main}
2019-04-22 15:25:00.103986+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154024] AsyncSerialTask---end
2019-04-22 15:25:00.104091+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154024] *********************************************************
2019-04-22 15:25:02.108899+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 1---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
2019-04-22 15:25:04.111910+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 1---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
2019-04-22 15:25:06.116733+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 2---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
2019-04-22 15:25:08.117706+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 2---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
2019-04-22 15:25:10.122737+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 3---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
2019-04-22 15:25:12.126742+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10181:2154074] 3---<NSThread: 0x600001992fc0>{number = 4, name = (null)}
通過我們的代碼測(cè)驗(yàn),可以看出:
- 三個(gè)追加的任務(wù)都是在一個(gè)新的線程中執(zhí)行的,在串行隊(duì)列中異步執(zhí)行任務(wù),會(huì)開啟一條新線程,由于隊(duì)列是串行的,所以任務(wù)是按序執(zhí)行的。
- 從我們的log中可以看到,
begin的log之后,馬上就是end的log,因此可以看出,它并不會(huì)阻塞當(dāng)前線程,并不需要等待追加的任務(wù)執(zhí)行完成。
異步執(zhí)行+主隊(duì)列
#pragma mark - 異步執(zhí)行+主隊(duì)列
/*
* 特點(diǎn):
* 1.在當(dāng)前線程(主線程)中執(zhí)行任務(wù)
* 2.按序執(zhí)行任務(wù),執(zhí)行行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
* 3.不會(huì)阻塞當(dāng)前線程
*/
- (IBAction)executeAsyncMainQueueTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncMainQueueTask---begin");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(mainQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_async(mainQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"AsyncMainQueueTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 18:28:03.990381+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] CurrentThread begin---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:03.990589+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] AsyncMainQueueTask---begin
2019-04-22 18:28:03.990808+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] CurrentThread end---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:03.990959+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] AsyncMainQueueTask---end
2019-04-22 18:28:03.991088+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] *********************************************************
2019-04-22 18:28:05.993620+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 1---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:07.994036+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 1---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:09.995547+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 2---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:11.997136+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 2---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:13.997717+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 3---<NSThread: 0x600003ee6900>{number = 1, name = main}
2019-04-22 18:28:15.998158+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[12894:2615623] 3---<NSThread: 0x600003ee6900>{number = 1, name = main}
通過我們的代碼測(cè)驗(yàn),可以看出:
3個(gè)任務(wù)在主線程中,按序執(zhí)行
從我們的log中可以看到,
begin的log之后,馬上就是end的log,因此可以看出,它并不會(huì)阻塞當(dāng)前線程,并不需要等待追加的任務(wù)執(zhí)行完成。
同步執(zhí)行+主隊(duì)列
#pragma mark - 同步執(zhí)行+主隊(duì)列
/*
* 特點(diǎn):
* 會(huì)直接產(chǎn)生死鎖
*/
- (IBAction)executeSyncMainQueueTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncMainQueueTask---begin");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(mainQueue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
dispatch_sync(mainQueue, ^{
// 追加任務(wù)3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncMainQueueTask---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 16:04:57.238644+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10673:2238846] CurrentThread begin---<NSThread: 0x6000038ca800>{number = 1, name = main}
2019-04-22 16:04:57.238915+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[10673:2238846] SyncMainQueueTask---begin
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
frame #0: 0x000000010d45fa19 libdispatch.dylib`__DISPATCH_WAIT_FOR_QUEUE__ + 444
frame #1: 0x00007ffee542deb0
frame #2: 0x000000010d45f3f0 libdispatch.dylib`_dispatch_sync_f_slow + 231
* frame #3: 0x000000010a7cff5a GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)`-[ViewController executeSyncMainQueueTask:](self=0x00007fb8f25124f0, _cmd="executeSyncMainQueueTask:", sender=0x00007fb8f2515fa0) at ViewController.m:220
frame #4: 0x000000010e9b9ecb UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 83
frame #5: 0x000000010e3f50bd UIKitCore`-[UIControl sendAction:to:forEvent:] + 67
frame #6: 0x000000010e3f53da UIKitCore`-[UIControl _sendActionsForEvents:withEvent:] + 450
frame #7: 0x000000010e3f431e UIKitCore`-[UIControl touchesEnded:withEvent:] + 583
frame #8: 0x000000010e9f50a4 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 2729
frame #9: 0x000000010e9f67a0 UIKitCore`-[UIWindow sendEvent:] + 4080
frame #10: 0x000000010e9d4394 UIKitCore`-[UIApplication sendEvent:] + 352
frame #11: 0x000000010eaa95a9 UIKitCore`__dispatchPreprocessedEventFromEventQueue + 3054
frame #12: 0x000000010eaac1cb UIKitCore`__handleEventQueueInternal + 5948
frame #13: 0x000000010bab8721 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #14: 0x000000010bab7f93 CoreFoundation`__CFRunLoopDoSources0 + 243
frame #15: 0x000000010bab263f CoreFoundation`__CFRunLoopRun + 1263
frame #16: 0x000000010bab1e11 CoreFoundation`CFRunLoopRunSpecific + 625
frame #17: 0x00000001141491dd GraphicsServices`GSEventRunModal + 62
frame #18: 0x000000010e9b881d UIKitCore`UIApplicationMain + 140
frame #19: 0x000000010a7d03c0 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)`main(argc=1, argv=0x00007ffee542ff90) at main.m:14
frame #20: 0x000000010d4c7575 libdyld.dylib`start + 1
(lldb)
通過我們的代碼測(cè)驗(yàn),可以看出:
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) frame #0: 0x000000010d45fa19 libdispatch.dylib`__DISPATCH_WAIT_FOR_QUEUE__ + 444應(yīng)用在主線程同步執(zhí)行第一個(gè)任務(wù)時(shí),就會(huì)直接crash,我們同步
LLDB的bt指定查看函數(shù)調(diào)用棧,可以發(fā)現(xiàn),在系統(tǒng)庫libdispatch調(diào)用__DISPATCH_WAIT_FOR_QUEUE__函數(shù)時(shí),就會(huì)產(chǎn)生一個(gè)由隊(duì)列引起的循環(huán)等待導(dǎo)致的crash,這就是我們常說的Deadlock死鎖,接下里我們來詳細(xì)介紹一下死鎖產(chǎn)生的原因與注意事項(xiàng)。
GCD中產(chǎn)生死鎖的原因
- (IBAction)executeSyncMainQueueTask:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"SyncMainQueueTask---begin");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
});
從上面的代碼我們可以看出,當(dāng)我們點(diǎn)擊按鈕,調(diào)用executeSyncMainQueueTask方法時(shí),這時(shí)我們其實(shí)是在主隊(duì)列(串行隊(duì)列)提交了一個(gè)任務(wù),我們暫先稱它為任務(wù)0然后我們又使用dispatch_sync同步執(zhí)行方法往主隊(duì)列中提交了任務(wù)1的Block,現(xiàn)在我們來分析一下,為什么這種情況下會(huì)產(chǎn)生死鎖
- 先往主隊(duì)列(串行隊(duì)列)中提交了任務(wù)0,然后在任務(wù)0執(zhí)行的過程中同步地往主隊(duì)列中添加了任務(wù)1
- 主隊(duì)列中添加的任務(wù)都會(huì)在主線程中執(zhí)行,同時(shí)按照串行隊(duì)列的特點(diǎn)(任務(wù)按序執(zhí)行),主線程中首先會(huì)執(zhí)行任務(wù)0,任務(wù)0執(zhí)行完成之后才會(huì)去執(zhí)行任務(wù)1,但是在任務(wù)0執(zhí)行的過程中,使用同步方式往主隊(duì)列中添加任務(wù)1,由于是使用同步方式,這時(shí)主線程會(huì)被阻塞,需要任務(wù)1完成之后,任務(wù)0才會(huì)繼續(xù)往下執(zhí)行。由此,我們可以看出,由于串行隊(duì)列的特性,任務(wù)1會(huì)依賴于任務(wù)0的執(zhí)行完成才會(huì)繼續(xù)往下執(zhí)行,同時(shí)由于同步添加任務(wù)的特性(會(huì)阻塞當(dāng)前線程,直到添加的任務(wù)執(zhí)行完成),任務(wù)0會(huì)依賴于任務(wù)1的執(zhí)行完成。所以,2個(gè)任務(wù)的執(zhí)行就會(huì)因?yàn)橄嗷サ却龑?duì)方的完成,而導(dǎo)致死鎖。
通過上面的分析,我們可以看出這里產(chǎn)生死鎖的一個(gè)很重要的原因就是主隊(duì)列是一個(gè)串行的隊(duì)列(主隊(duì)列中只有一條主線程)。如果我們?nèi)缦吕?,在并發(fā)隊(duì)列中提交,則不會(huì)造成死鎖:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ //這里是為了保證當(dāng)前任務(wù)是處于并發(fā)隊(duì)列開辟的線程中,而不是主線程中
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)0");
});
NSLog(@"任務(wù)1");
});
原因是并發(fā)隊(duì)列中的任務(wù)執(zhí)行時(shí)并行的,所以,任務(wù)1并不會(huì)一直等待任務(wù)0執(zhí)行完成,才去執(zhí)行,而是直接執(zhí)行完。因此任務(wù)0因?yàn)槿蝿?wù)1的結(jié)束,線程阻塞也會(huì)被消除,任務(wù)0得以繼續(xù)執(zhí)行。
我們?cè)匍_看一組示例:
//目前處于主線程中
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)0");
});
NSLog(@"任務(wù)1");
我們?cè)谥骶€程中,往全局隊(duì)列同步提交了Block,因?yàn)槿株?duì)列和主隊(duì)列是兩個(gè)隊(duì)列,所以任務(wù)1的執(zhí)行,并不需要等待任務(wù)0。所以等任務(wù)0結(jié)束,任務(wù)1也可以被執(zhí)行。
當(dāng)然這里因?yàn)樘峤籅lock所在隊(duì)列,Block被執(zhí)行的隊(duì)列是完全不同的兩個(gè)隊(duì)列,所以這里用串行queue,也是不會(huì)死鎖的,到這里我們也可以知道一些同步提交(dispatch_sync)的阻塞機(jī)制:
同步提交Block,首先是阻塞的當(dāng)前提交Block的線程,而在隊(duì)列中,同步提交的Block,只會(huì)阻塞串行隊(duì)列(由串行隊(duì)列的同一時(shí)間只能執(zhí)行一個(gè)任務(wù)的特性決定),并不會(huì)阻塞并發(fā)隊(duì)列,當(dāng)然dispatch_barrier系列的除外,這個(gè)我會(huì)在后面的文章中講到,歡迎大家繼續(xù)關(guān)注我的博客
現(xiàn)在我們可以用一句話來總結(jié)產(chǎn)生死鎖的原因就是:
使用同步方式(
dispatch_sync)提交一個(gè)任務(wù)到一個(gè)串行隊(duì)列時(shí),如果提交這個(gè)任務(wù)的操作所處的線程,也是處于這個(gè)串行隊(duì)列,就會(huì)引起死鎖
開發(fā)中如何避免產(chǎn)生死鎖
- 不要在主線程中使用同步方式添加任務(wù)到主隊(duì)列
- 不要嵌套使用在自定義的串行隊(duì)列中,嵌套使用同步方式添加任務(wù)到該串行隊(duì)列
6種組合使用總結(jié)
| 總結(jié) | 串行隊(duì)列 | 并發(fā)隊(duì)列 | 主隊(duì)列 |
|---|---|---|---|
| 同步添加(sync) | 不開辟新線程,在當(dāng)前線程中串行執(zhí)行任務(wù) | 不開辟新線程,在當(dāng)前線程中串行執(zhí)行任務(wù) | 死鎖 |
| 異步添加(async) | 開辟新線程(1條),串行執(zhí)行任務(wù) | 開辟新線程(1/n條),并發(fā)執(zhí)行任務(wù) | 不開辟新線程,在主線程中順序執(zhí)行 |
線程間通信
#pragma mark - 子線程執(zhí)行耗時(shí)代碼,主線程更新UI
- (IBAction)threadInteraction:(UIButton *)sender {
NSLog(@"CurrentThread begin---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"threadInteraction---begin");
//異步添加任務(wù)到全局并發(fā)隊(duì)列執(zhí)行耗時(shí)操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時(shí)任務(wù)
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
}
//回到主線程更新UI
dispatch_sync(dispatch_get_main_queue(), ^{
//Do something here to update UI
});
});
NSLog(@"CurrentThread end---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"threadInteraction---end");
NSLog(@"*********************************************************");
}
執(zhí)行結(jié)果如下:
2019-04-22 18:47:54.836027+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669381] CurrentThread begin---<NSThread: 0x6000038e9400>{number = 1, name = main}
2019-04-22 18:47:54.836217+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669381] threadInteraction---begin
2019-04-22 18:47:54.836436+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669381] CurrentThread end---<NSThread: 0x6000038e9400>{number = 1, name = main}
2019-04-22 18:47:54.836619+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669381] threadInteraction---end
2019-04-22 18:47:54.836761+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669381] *********************************************************
2019-04-22 18:47:56.839416+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669427] 1---<NSThread: 0x6000038b6d40>{number = 4, name = (null)}
2019-04-22 18:47:58.840646+0800 GCD(一) 隊(duì)列、任務(wù)、串行、并發(fā)[13229:2669427] 1---<NSThread: 0x6000038b6d40>{number = 4, name = (null)}
在平常的開發(fā)中,我們最常用的就是提交一個(gè)任務(wù)到全局并發(fā)隊(duì)列取執(zhí)行一些比較耗時(shí)的操作(比如,文件下載、文件上傳、圖片解碼、數(shù)據(jù)庫操作、IO讀寫),然后再切回到主線程去更新UI,蘋果官方限定了更新UI的操作只能在主線程中執(zhí)行,所以,我們最后還是要回到主線程去處理我們的UI交互。
本文到這里已經(jīng)基本結(jié)束,在接下來的文章中,我將會(huì)繼續(xù)講解GCD多線程編程的另外幾個(gè)知識(shí)點(diǎn),也是我們平時(shí)開發(fā)中實(shí)際很經(jīng)常會(huì)用到的,如dispatch_barrier、dispatch_group、dispatch_semaphore、線程安全的相關(guān)內(nèi)容
如果文中有錯(cuò)誤的地方,或者與你的想法相悖的地方,請(qǐng)?jiān)谠u(píng)論區(qū)告知我,我會(huì)繼續(xù)改進(jìn),如果你覺得這個(gè)篇文章總結(jié)的還不錯(cuò),麻煩動(dòng)動(dòng)小手,給我的文章與Git代碼樣例點(diǎn)個(gè)?