iOS從實際出發(fā)理解多線程
前言
多線程很多開發(fā)者多多少少相信也都有了解,以前有些東西理解的不是很透,慢慢的積累之后,這方面的東西也需要自己好好的總結(jié)一下。多線程從我剛接觸到iOS的時候就知道這玩意挺重要的,但那時也是能力有限,沒辦法很好的理解它,要是只是查它的概念性的東西,網(wǎng)上一搜一大把,我們再那樣去總結(jié)就顯得意義不大了。這篇文章從我剛開始構(gòu)思著去寫的時候,就希望自己能換個角度去寫,想從實際問題出發(fā)總結(jié)多線程,那就從第三方以及自己看到的一些例子還有前段時間讀的多線程和內(nèi)存管理的書中分析理解總結(jié)一下多線程。
這幾個概念很容易繞暈
一 進程:進程就是線程的容器,你打開一個App就是打開了一個進程,QQ有QQ的進程,微信有微信的進程,一個進程可以包含多個線程,要是把進程比喻成一條高速公路,線程就是高速路上的一條條車道,也正是因為有了這些車道,整個交通的運行效率變得更高,也正是因為有了多線程的出現(xiàn),整個系統(tǒng)運行效率變得更高。
二 線程:線程就是在進程中我么開辟的一條條為我們做事的進程實體,總結(jié)的通俗一點,線程就是我們在進程上開辟的一條條做我們想做的事的通道。 一條線程在一個時間點上只能做一件“事”,多線程在同一時間點上,就能做多件“事”,這個理解,還是我們前面說的高速路的例子。
一條高速路是一個進程, 一條條車道就是不同的線程,在過收費站的時候,這條進程上要是只有一條線程,也就是一條高速路上只有一個車道,那你就只能排隊一輛一輛的通過,同一時間不可能有兩輛車一起過去,但要是你一個進程上有多個線程,也就是高速路上有幾個車道,也就有多個窗口收費,這樣的話同一時間就完全有可能兩輛車一起交完費通過了,這樣說相信也能理解這個進程和線程的關(guān)系了。
- 同步線程:同步線程會阻塞當(dāng)前的線程去執(zhí)行同步線程里面想做的“事”(任務(wù)),執(zhí)行完之后才會返回當(dāng)前線程。
- 異步線程:異步線程不會阻塞當(dāng)前的線程去執(zhí)行異步線程里面想做的“事”,因為是異步,所以它會重新開啟一個線程去做想做的“事”。
三 隊列:隊列就是用來管理下面說的“任務(wù)”的,它采用的是先進先出(FIFO)的原則,它衍生出來的就是下面的它的分類并行和串行隊列,一條線程上可以有多個隊列。
- 并行隊列:這個隊列里面的任務(wù)是可以并發(fā)(同時)執(zhí)行的,由于我們知道,同步執(zhí)行任務(wù)不會開啟新的線程,所以并行隊列同步執(zhí)行任務(wù)任務(wù)只會在一條線程里面同步執(zhí)行這些任務(wù),又由于同步執(zhí)行也就是在當(dāng)前線程中做事,這個時候就需要一件一件的讓“事”(任務(wù))做完在接著做下一個。但要是是并發(fā)隊列異步執(zhí)行,就對應(yīng)著開啟異步線程執(zhí)行要做的“事”(任務(wù)),就會同一時間又許多的“事”被做著。
- 串行隊列:這個隊列里面的任務(wù)是串行也就是一件一件做的,串行同步會一件一件的等事做完再接著做下一件,要是異步的就會開啟一條新的線程串行的執(zhí)行我們的任務(wù)。
四 任務(wù):任務(wù)按照自己通俗一點的理解,就是提到的“事”這個概念,這個“事”就可以理解為任務(wù),那這個“事”也肯定是在線程上面執(zhí)行的(不管是在當(dāng)前線程還是你另開啟的線程)。這個“事”你可以選擇同步或者而是異步執(zhí)行,這就衍生出了東西也就契合線程上面的同步線程和異步線程。
- 同步任務(wù):不需要開啟新的線程,在當(dāng)前線程執(zhí)行就可以。
- 異步任務(wù):你需要開辟一條新的線程去異步的執(zhí)行這個任務(wù)。
iOS當(dāng)中還有一個特殊的串行隊列-- 主隊列, 這個主隊列中運行著一條特殊的線程 -- 主線程
主線程又叫UI線程,UI線程顧名思義主要的任務(wù)及時處理UI,也只有主線程有處理UI的能力,其他的耗時間的操作我們就放在子線程(也就是開辟線程)去執(zhí)行,開線程也會占據(jù)一定的內(nèi)存的,所以不要同時開啟很多的線程。
通過上面的內(nèi)容解釋了多線程里面幾個關(guān)鍵的概念的東西,要是有不理解的地方歡迎多交流,下面再給出隊列執(zhí)行時候的一個運行的表格,我們一個一個慢慢的解釋。

NSThread
其實在我們?nèi)粘5拈_發(fā)中NSThread使用也是挺多的,具體關(guān)于它的一些我們需要注意的地方我們一步步的開始說,先看看它的初始化的幾個方法
- /*
- 初始化NSThread的類方法,具體的任務(wù)在Block中執(zhí)行
- + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- 利用selector方法初始化NSThread,target指selector方法從屬于的對象 selector方法也是指定的target對象的方法
- + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
- 初始化NSThread的方法,這兩個方法和上面兩個方法的區(qū)別就是這兩個你能獲取到NSThread的對象
- 具體的參數(shù)和前面解釋的參數(shù)意義都是一樣的
- 切記一點: 下面兩個方法初始化的NSThread你需要手動start開啟線程
- - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
- - (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- */
除了上面四個我們提出的方法,我們在初始化這個問題上還需要注意的還有一點,就是 NSObject (NSThreadPerformAdditions) ,為我們的NSObject添加的這個類別,它里面的具體的一些方法我們也是很常用的:
- /*
- 這個方法你執(zhí)行的aSelector就是在MainThread執(zhí)行的,也就是在主線程
- 注意這里的waitUntilDone這個后面的BOOL類型的參數(shù),這個參數(shù)表示是否等待一直到aSelector這個方法執(zhí)行結(jié)束
- modes是RunLoop的運行的類型這個RunLoop我也會好好在總結(jié)后面
- - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
- - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
- // equivalent to the first method with kCFRunLoopCommonModes
- 上面的兩個方法是直接在主線程里面運行,下面的這兩個方法是要在你初始化的thr中去運行,其他的參數(shù)和上面解釋的一樣
- - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array NS_AVAILABLE(10_5, 2_0);
- - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
- // equivalent to the first method with kCFRunLoopCommonModes
- - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);
- */
我們在說說前面說的waitUntilDone后面的這個BOOL類型的參數(shù),這個參數(shù)的意義有點像我們是否同步執(zhí)行aSelector這個任務(wù)!具體的看下面兩張圖的內(nèi)容就一目了然了:

在看看等于YES的時候結(jié)果的輸出情況:

關(guān)于NSThread我們再說下面幾個方法的具體的含義就不在描述了,關(guān)于NSThread有什么其他的問題,可以加我QQ交流:
- /*
- 設(shè)置線程沉睡到指定日期
- + (void)sleepUntilDate:(NSDate *)date;
- 線程沉睡時間間隔,這個方法在設(shè)置啟動頁間隔的時候比較常見
- + (void)sleepForTimeInterval:(NSTimeInterval)ti;
- 線程退出,當(dāng)執(zhí)行到某一個特殊情況下的時候你可以退出當(dāng)前的線程,注意不要在主線程隨便調(diào)用
- + (void)exit;
- 線程的優(yōu)先級
- + (double)threadPriority;
- 設(shè)置線程的優(yōu)先級
- + (BOOL)setThreadPriority:(double)p;
- */
NSOperation
多線程我們還得提一下NSOperation,它可能比我們認(rèn)識中的要強大一點,NSOperation也是有很多東西可以說的,前面的NSThread其實也是一樣,這些要是仔細(xì)說的話都能寫一篇文章出來,可能以后隨著自己接觸的越來越多,關(guān)于多線程這一塊的東西我們會獨立的創(chuàng)建一個分類總結(jié)出去。
首先得知道NSOperation是基于GCD封裝的,NSOperation這個類本身我們使用的時候不躲,更多的是集中在蘋果幫我們封裝好的NSInvocationOperation和NSBlockOperation
你command一下NSOperation進去看看,有幾個點你還是的了解一下的,主要的就是下面的幾個方法:
- NSOperation * operation = [[NSOperation alloc]init];
- [operation start]; //開始
- [operation cancel]; //取消
- [operation setCompletionBlock:^{
- //operation完成之后的操作
- }];
我們具體的說一下我們上面說的兩個類:NSInvocationOperation和NSBlockOperation,先看看NSInvocationOperation的初始化:
- /*
- 初始化方法 看過前面的文章之后它的target 、sel 、arg 等參數(shù)相信不難理解
- -(nullable instancetype)initWithTarget:(id)target selector:(SEL)sel object:(nullable id)arg;
- -(instancetype)initWithInvocation:(NSInvocation *)inv NS_DESIGNATED_INITIALIZER;
- */
補充: NS_DESIGNATED_INITIALIZER 指定初識化方法并不是對使用者。而是對內(nèi)部的現(xiàn)實,可以點擊進去具體了解一下它!NSInvocationOperation其實是同步執(zhí)行的,因此單獨使用的話就價值不大了,它和NSOperationQueue一起去使用才能實現(xiàn)多線程調(diào)用。這個我們后面再具體的說
在看看NSBlockOperation這個,它重要的方法就我們下面的兩個
- /*
- 初始化方法
- + (instancetype)blockOperationWithBlock:(void (^)(void))block;
- 添加一個可以執(zhí)行的block到前面初始化得到的NSBlockOperation中
- - (void)addExecutionBlock:(void (^)(void))block;
- */
NSBlockOperation這個我們得提一點: 它的最大的并發(fā)具體的最大并發(fā)數(shù)和運行環(huán)境也是有關(guān)系的,具體的內(nèi)容我們可以戳戳這里同行總結(jié)以及驗證的,我們由于篇幅的原因就不在這里累贅。
其實只要是上面這些的話是不夠我們?nèi)粘J褂玫?,但還有一個激活他們倆的類我們也得說說:NSOPerationQueue 下面是關(guān)于它的大概的一個說明,都挺簡單,就不在特意寫Demo。

關(guān)于NSOperation的我們就說這么多,下面重點說一下GCD。
主角GCD -- 主線程
1、我們先從主隊列,主線程開始說起,通過下面的方法我們就可以獲取得到主隊列:
- dispatch_queue_t mainqueue = dispatch_get_main_queue();
2、我們在主線程同步執(zhí)行任務(wù),下面是操作的結(jié)果以及打印的信息:

我們解釋一下為什么在主線程中執(zhí)行同步任務(wù)會出現(xiàn)這個結(jié)果,我們一步一步的梳理一下這個執(zhí)行過程:
- 獲取到在主隊列主線程中執(zhí)行了最前面的打印信息,這個沒什么問題
- 開始執(zhí)行dispatch_sync這個函數(shù),主隊列是串行隊列,這個函數(shù)會把這個任務(wù)插入到主隊列的最后面(理解隊列添加任務(wù))
- 主線程執(zhí)行到這里的時候就會等待插入的這個同步任務(wù)執(zhí)行完之后再執(zhí)行后面的操作
- 但由于這個同步任務(wù)是插入到主隊列的最后面,最隊列前面的任務(wù)沒有執(zhí)行完之前是不會執(zhí)行這個block的(主線程在執(zhí)行initMainQueue任務(wù))
- 這樣就造成了一個相互等待的過程,主線程在等待block完返回,block卻在等待主線程執(zhí)行它,這樣就造成了死鎖,看打印的信息你也就知道block是沒有被執(zhí)行的。
這里我們你可能會思考,主隊列是一個串行隊列,那我們在主線程中添加一個串行隊列,再給串行隊列添加一個同步任務(wù),這時候和前面主線程主隊列添加同步任務(wù)不就場景一樣了嗎?那結(jié)果呢? 我們看看下面的打?。?/p>

我們按照前面的方式解釋一下這個的執(zhí)行步驟:
- 主線程在執(zhí)行主隊列中的方法initSerialQueue,到這個方法時候創(chuàng)建了一個串行隊列(注意不是主隊列)打印了前面的第一條信息
- 執(zhí)行到dispatch_sync函數(shù),這個函數(shù)給這個串行隊列中添加了一個同步任務(wù),同步任務(wù)是會立馬執(zhí)行的
- 主線程就直接操作執(zhí)行了這個隊列中的同步任務(wù),打印的第二條信息
- 主線程接著執(zhí)行下面的第三條打印信息
理解:看這個執(zhí)行的過程對比前面的,你就知道了不同的地方就是前面是添加在了主隊列當(dāng)中,但這里有添加到主隊列,由于是插入到主隊列的末尾,所以需要主隊列的任務(wù)都執(zhí)行完才能指定到它,但主線程執(zhí)行到initMainQueue這個方法的時候在等待這個方法中添加的同步任務(wù)執(zhí)行完接著往下執(zhí)行,但它里面的同步任務(wù)又在等待主線程執(zhí)行完在執(zhí)行它,就相互等待了,但主線程執(zhí)行不是主隊列里面的同步任務(wù)的時候是不需要主線程執(zhí)行完所有操作在執(zhí)行這個任務(wù)的,這個任務(wù)是它添加到串行隊列的開始也是結(jié)束的任務(wù),由于不需要等待,就不會造成死鎖!
上面這個問題經(jīng)常會看到有人問,有許多解釋,也希望自己能把這個問題給說清楚了!
3、主線程這里我們再提一點,就是線程間的信息簡單傳遞
前面我們有說到主線程又叫做UI線程,所有關(guān)于UI的事我們都是在主線程里面更新的,像下載數(shù)據(jù)以及數(shù)據(jù)庫的訪問等這些耗時的操作我們是建議放在子線程里面去做,那就會產(chǎn)生子線程處理完這些之后要回到主線程更行UI的問題上,這一點值得我們好好的注意一下,但其實這一點也是我們用的最多的,相信大家也都理解!
主角GCD -- 串行隊列
串行隊列的概念性的東西我們就不在這里累贅,不管是串行隊列+同步任務(wù)還是串行隊列+異步任務(wù)都簡單,有興趣可以自己是這寫一下,后面分析會提到他們的具體使用的,我們在一個稍微比前面的說的復(fù)雜一點點的問題,串行隊列+異步+同步,可以先試著不要往下面看先分析一下下面這段代碼的執(zhí)行結(jié)果是什么?
- static void * DISPATCH_QUEUE_SERIAL_IDENTIFY;
- -(void)initDiapatchQueue{
- dispatch_queue_t serialQueue = dispatch_queue_create(DISPATCH_QUEUE_SERIAL_IDENTIFY, DISPATCH_QUEUE_SERIAL);
- dispatch_async(serialQueue, ^{
- NSLog(@"一個異步任務(wù)的內(nèi)容%@",[NSThread currentThread]);
- dispatch_sync(serialQueue, ^{
- NSLog(@"一個同步任務(wù)的內(nèi)容%@",[NSThread currentThread]);
- });
- });
- }
不知道你分析數(shù)來的這點代碼的結(jié)果是什么,我們這里來看看結(jié)果,然后和上面一步一步的分析一下它的整個的執(zhí)行過程,就能找到答案:

答案就是crash了,其實也是死鎖,下面一步一步的走一下這整個過程,分析一下哪里死鎖了:
- 主線程主隊列中執(zhí)行任務(wù)initDispatchQueue,進入了這個方法,在這個方法里面創(chuàng)建了一個串行隊列,這一步相信大家都明白,沒什么問題。
- 給這個串行隊列添加了一個異步任務(wù),由于是異步任務(wù),所以會開啟一條新的線程,為了方便描述,我們把新開的這個線程記做線程A, 把這個任務(wù)記做任務(wù)A,也由于是異步任務(wù),主線程就不會等待這個任務(wù)返回,就接著往下執(zhí)行其他任務(wù)了。
- 接下來的分析就到了這個線程A上,這個任務(wù)A被添加到串行隊列之后就開始在線程A上執(zhí)行,打印出了我們的第一條信息,也證明了不是在主線程,這個也沒問題。
- 線程A開始執(zhí)行這個任務(wù)A,進入這個任務(wù)A之后在這個任務(wù)A里面又同步在串行隊列里面添加任務(wù),記做任務(wù)B,由于任務(wù)B是dispatch_sync函數(shù)同步添加的,需要立馬被執(zhí)行,就等待線程A執(zhí)行它
- 但是這個任務(wù)B是添加到串行隊列的末尾的,線程A在沒有執(zhí)行完當(dāng)前任務(wù)A是不會去執(zhí)行它的,這樣就造成線程A在等待當(dāng)前任務(wù)A執(zhí)行完,任務(wù)B又在等待線程A執(zhí)行它,就形成了死鎖
經(jīng)過上面的分析,你就能看到這個場景和你在主線程同步添加任務(wù)是一樣的,我們再仔細(xì)的考慮一下這整個過程,在分析一下上面主線程+串行隊列+同步任務(wù)為什么沒有形成死鎖!相互對比理解,就能把整個問題想明白。
主角GCD -- 并行隊列
下面我們接著再說說這個并行隊列,并行隊列+同步執(zhí)行或者并行隊列+異步執(zhí)行這個我們也就沒什么好說的了,在這里說說并行+異步的需要注意的地方,不知道大家有沒有想過,并行的話很多任務(wù)會一起執(zhí)行,要是異步任務(wù)的話會開啟新的線程,那是不是我們添加了十個異步任務(wù)就會開啟十條線程呢?那一百個異步任務(wù)豈不是要開啟一百條線程,答案肯定是否定的!那系統(tǒng)到底是怎么處理的,我們也說說,下面的是高級編程書里面的解釋我們梳理一下給出結(jié)論。
- 當(dāng)為DISPATCH_QUEUE_CONCURRENT的時候,不用等待前面任務(wù)的處理結(jié)束,后面的任務(wù)也是能夠直接執(zhí)行的
- 并行執(zhí)行的處理數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài),即iOS和OS X基于Dispatch Queue中的處理數(shù)、CPU核數(shù)以及CPU負(fù)荷等當(dāng)前系統(tǒng)狀態(tài)來決定DISPATCH_QUEUE_CONCURRENT中并行執(zhí)行的處理數(shù)
- iOS 和 OS X的核心 -- XNU內(nèi)核決定應(yīng)當(dāng)使用的線程數(shù),并且生成所需的線程執(zhí)行處理
- 當(dāng)處理結(jié)束,應(yīng)當(dāng)執(zhí)行的處理數(shù)減少時,XNU內(nèi)核會結(jié)束不在需要的線程
處理并行異步任務(wù)時候線程是可以循環(huán)往復(fù)使用的,比如任務(wù)1的線程執(zhí)行完了任務(wù)1,線程可以接著去執(zhí)行后面沒有執(zhí)行的任務(wù)
這里的東西就這些,我們在前面串行隊列的時候,串行隊列+異步任務(wù)嵌套同步任務(wù)會造成死鎖,那我們要是把它變成同步隊列呢?結(jié)果又會是什么樣子呢?我們看看下面這段代碼的執(zhí)行結(jié)果:

從上面的結(jié)果可以看得出來,是沒有問題的,這里我們就不在一步一步的分析它的執(zhí)行過程了,就說說為什么并行的隊列就沒有問題,但是串行的隊列就會出問題:
并行隊列添加了異步任務(wù)也是創(chuàng)建了一個新的線程,然后再在這個任務(wù)里面給并行隊列添加一個同步任務(wù),由于是并行隊列 ,執(zhí)行這個同步任務(wù)是不需要前面的異步任務(wù)執(zhí)行完了,就直接開始執(zhí)行,所以也就有了下面的打印信息,通過上面幾個問題,相信理解了之后,對于串行隊列或者并行隊列添加同步任務(wù)或者異步任務(wù)都有了一個比較深的理解了,我們再接著往下總結(jié)。
GCD不僅僅這些
關(guān)于GCD的內(nèi)容還有下面這些都是值得我們關(guān)注的,下面我們開始一一說一說:
1、dispatch_barrier_async
dispatch_barrier_async 函數(shù)是我們俗稱的柵欄方法,“柵欄”的意思理解一下字面的,就是把外面和里面阻隔開,這個函數(shù)的作用就是這樣,把插入的這個柵欄之前和之后的阻隔開,等前面的執(zhí)行完了就執(zhí)行“柵欄函數(shù)”插入的任務(wù),等柵欄的任務(wù)執(zhí)行結(jié)束了就開始執(zhí)行柵欄后面的任務(wù)??聪旅嬉粋€簡單的Demo就理解了。

從上面就可以看到,我們把0插入到第三個任務(wù)的位置,它是等前面的兩個任務(wù)執(zhí)行完了,在去執(zhí)行第三個,要是你覺得這里前兩個任務(wù)簡單,執(zhí)行不需要太多的時間的話,你可以試著把前面兩個任務(wù)的“任務(wù)量”設(shè)置大一點,這樣有助于你更好的理解這個“柵欄”操作!
2、dispatch_after
dispatch_after 延時操作
如果某一條任務(wù)你想等多少時間之后再執(zhí)行的話,你就完全可以使用這個函數(shù)處理,寫法很簡單,因為已經(jīng)幫我們封裝好了,看下面這兩行代碼:
- // DISPATCH_TIME_NOW 當(dāng)前時間開始
- // NSEC_PER_SEC 表示時間的宏,這個可以自己上網(wǎng)搜索理解
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- NSLog(@"延遲了10秒執(zhí)行");
- });
3、dispatch_apply
dispatch_apply 類似一個for循環(huán),會在指定的dispatch queue中運行block任務(wù)n次,如果隊列是并發(fā)隊列,則會并發(fā)執(zhí)行block任務(wù),dispatch_apply是一個同步調(diào)用,block任務(wù)執(zhí)行n次后才返回。 由于它是同步的,要是我們下面這樣寫就會有出問題:

可以看到出問題了,但我們要是把它放在串行隊列或者并行隊列就會是下面這樣的情況

4、dispatch_group_t
dispatch_group_t的作用我們先說說,在追加到Dispatch Queue 中的多個任務(wù)全部結(jié)束之后想要執(zhí)行結(jié)束的處理,這種情況也會經(jīng)常的出現(xiàn),在只使用一個Serial Dispatch Queue時,只要將想執(zhí)行的操作全部追加該Serial Dispatch Queue中并且追加在結(jié)束處理就可以實現(xiàn),但是在使用 Concurrent Dispatch Queue 時或者同時使用多個 Dispatch Queue時候,就比較的復(fù)雜了,在這樣的情況下 Dispatch Group 就可以發(fā)揮它的作用了??纯聪旅娴倪@段代碼:
- -(void)testDispatch_group_t{
- dispatch_group_t group_t = dispatch_group_create();
- dispatch_queue_t queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_group_async(group_t, queue_t, ^{
- NSLog(@"1--當(dāng)前的線程%@",[NSThread currentThread]);
- });
- dispatch_group_async(group_t, queue_t, ^{
- NSLog(@"2--當(dāng)前的線程%@",[NSThread currentThread]);
- });
- dispatch_group_async(group_t, queue_t, ^{
- NSLog(@"3--當(dāng)前的線程%@",[NSThread currentThread]);
- });
- dispatch_group_async(group_t, queue_t, ^{
- for (int i = 1; i<10; i++) {
- 6
- NSLog(@"4--當(dāng)前的線程%@",[NSThread currentThread]);
- }
- });
- // 當(dāng)前的所有的任務(wù)都執(zhí)行結(jié)束
- dispatch_group_notify(group_t, queue_t, ^{
- NSLog(@"前面的全都執(zhí)行結(jié)束了%@",[NSThread currentThread]);
- });
- }
這段代碼的意圖很明顯,看了下面的打印信息這個你也就理解它了:

總結(jié): 關(guān)于多線程的最基本的問題暫時先總結(jié)這么多,還有許多的問題,自己也在總結(jié)當(dāng)中,比如以下線程鎖等等的問題,等總結(jié)到差不多的時候再分享!