iOS 多線程總結(jié)(上)

一辈赋、前言

多線程是在 iOS 里非常重要的一塊兒知識點,我最近學(xué)習(xí)了李明杰大神的多線程相關(guān)視頻冀墨,對自己的多線程相關(guān)知識進(jìn)行了查缺補(bǔ)漏,受益良多涛贯,在此根據(jù)所學(xué)進(jìn)行簡單的記錄诽嘉,希望能幫助更多伙伴,也能作為自己模糊了以后查閱使用。
本篇文章分為上下兩篇虫腋。

二骄酗、iOS中常見的多線程方案

技術(shù)方案 簡介 語言 線程生命周期 使用頻率
pthread ? 一套通用的多線程API
? 適用于Unix\Linux\Windows等系統(tǒng)
? 跨平臺\可移植
? 使用難度大
C 程序員管理 幾乎不用
NSThread ? 使用更加面向?qū)ο?br>? 簡單易用,可直接操作線程對象 OC 程序員管理 偶爾使用
GCD ? 旨在替代NSThread等線程技術(shù)
? 充分利用設(shè)備的多核
C 自動管理 \color{red}{經(jīng)常使用}
NSOperation ? 基于GCD(底層是GCD)
? 比GCD多了一些更簡單實用的功能
? 使用更加面向?qū)ο?/td>
OC 自動管理 \color{red}{經(jīng)常使用}

二悦冀、同步趋翻、異步、串行盒蟆、并發(fā)

1踏烙、GCD 中有 2 個用來執(zhí)行任務(wù)的函數(shù)

  • 用同步的方式執(zhí)行任務(wù)
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    ? queue:隊列
    ? block:任務(wù)

2、GCD 的隊列可以分為 2 大類型

  • \color{red}{并發(fā)}隊列(Concurrent Dispatch Queue)
    ? 可以讓多個任務(wù)\color{blue}{并發(fā)}(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
    ? \color{blue}{并發(fā)}功能只有在\color{blue}{異步}(dispatch_\color{blue}{async})函數(shù)下才有效

  • \color{red}{串行}隊列(Serial Dispatch Queue)
    ? 讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后历等,再執(zhí)行下一個任務(wù))

  • 用異步的方式執(zhí)行任務(wù)
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

  • GCD 源碼:https://github.com/apple/swift-corelibs-libdispatch

3讨惩、容易混淆的術(shù)語

  • 4 個術(shù)語比較容易混淆:\color{blue}{同步、異步}寒屯、\color{red}{并發(fā)荐捻、串行}

  • \color{blue}{同步}\color{blue}{異步}主要影響:能不能開啟新的線程
    ? \color{blue}{同步}:在\color{green}{當(dāng)前}線程中執(zhí)行任務(wù),\color{green}{不具備}開啟新線程的能力
    ? \color{blue}{異步}:在\color{green}{新的}線程中執(zhí)行任務(wù)寡夹,\color{green}{具備}開啟新線程的能力

  • 并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式
    ? \color{red}{并發(fā)}\color{blue}{多個}任務(wù)并發(fā)(同時)執(zhí)行
    ? \color{red}{串行}\color{blue}{一個}任務(wù)執(zhí)行完畢后靴患,再執(zhí)行下一個任務(wù)

dispatch_sync\color{red}{是立馬在當(dāng)前線程執(zhí)行完里面的任務(wù)。}
dispatch_sync 和 dispatch_async 用來控制是否要開啟新的線程要出。

隊列的類型鸳君,決定了任務(wù)的執(zhí)行方法(并發(fā)、串行)
1患蹂、并發(fā)隊列
2或颊、串行隊列
3、主隊列(也是一個串行隊列)

4传于、各種隊列的執(zhí)行效果:

并發(fā)隊列 手動創(chuàng)建的串行隊列 主隊列
同步( sync ) \color{red}{沒有}開啟新線程
\color{blue}{串行}執(zhí)行任務(wù)
\color{red}{沒有}開啟新線程
\color{blue}{串行}執(zhí)行任務(wù)
\color{red}{沒有}開啟新線程
\color{blue}{串行}執(zhí)行任務(wù)
異步( async ) \color{green}{有}開啟新線程
\color{orange}{并發(fā)}執(zhí)行任務(wù)
\color{green}{有}開啟新線程
\color{blue}{串行}執(zhí)行任務(wù)
\color{red}{沒有}開啟新線程
\color{blue}{串行}執(zhí)行任務(wù)
  • 使用 sync 函數(shù)往\color{red}{當(dāng)前}\color{blue}{串行}隊列中添加任務(wù)囱挑,會卡住當(dāng)前的串行隊列(產(chǎn)生死鎖)

5、問題

問題1:

以下代碼是在主線程執(zhí)行的沼溜,會不會產(chǎn)生死鎖平挑?

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"執(zhí)行任務(wù)1");
  dispatch_queue_t queue = dispatch_get_main_queue();
  dispatch_sync(queue, ^{
    NSLog(@"執(zhí)行任務(wù)2");
  });
  NSLog(@"執(zhí)行任務(wù)3");
}

答案:會!執(zhí)行順序是1崩潰系草。
分析:
1通熄、隊列的特點:排隊,F(xiàn)IFO(Fisrst In First Out找都,先進(jìn)先出)
2唇辨、dispatch_sync 同步隊列特點:立馬在當(dāng)前線程執(zhí)行任務(wù),執(zhí)行完畢才能繼續(xù)往下執(zhí)行能耻。
因為任務(wù)2是在同步執(zhí)行赏枚,并且在主隊列中亡驰。而viewDidLoad也是在主隊列中。
所以主隊列中的任務(wù)2會等viewDidLoad這個任務(wù)先執(zhí)行完再執(zhí)行饿幅,而viewDidLoad這個任務(wù)需要執(zhí)行完任務(wù)3才算執(zhí)行完凡辱。所以就出現(xiàn)了死鎖。

如下圖所示:
問題2:

以下代碼是在主線程執(zhí)行的栗恩,會不會產(chǎn)生死鎖透乾?(和第1題的區(qū)別只是把sync改為了async)

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"執(zhí)行任務(wù)1");
  dispatch_queue_t queue = dispatch_get_main_queue();
  dispatch_async(queue, ^{
    NSLog(@"執(zhí)行任務(wù)2");
  });
  NSLog(@"執(zhí)行任務(wù)3");
}

答案:不會!執(zhí)行順序是132摄凡。
分析:異步雖然因為是主隊列不會開啟新線程续徽,但因為是異步的蚓曼,所以不是必須馬上取出任務(wù)2執(zhí)行再執(zhí)行后面的任務(wù)3亲澡,所以可以最后再取出任務(wù)2執(zhí)行。

問題3:

以下代碼是在主線程執(zhí)行的纫版,會不會產(chǎn)生死鎖床绪?

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"執(zhí)行任務(wù)1");

  dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
  dispatch_async(queue, ^{ // Block 0
    NSLog(@"執(zhí)行任務(wù)2");

    dispatch_sync(queue, ^{  // Block 1
      NSLog(@"執(zhí)行任務(wù)3");
    });

    NSLog(@"執(zhí)行任務(wù)4");
  });
  NSLog(@"執(zhí)行任務(wù)5");
}

答案:會!執(zhí)行順序是152崩潰其弊。
分析:dispatch_sync\color{red}{是立馬在當(dāng)前線程執(zhí)行完里面的任務(wù)癞己。}
代碼中注釋標(biāo)明了 \color{blue}{Block 0}\color{green}{Block 1}
要想執(zhí)行完\color{blue}{Block 0}梭伐,必須執(zhí)行完\color{green}{Block 1}(因為是sync)才能執(zhí)行后面的任務(wù)4痹雅,才算執(zhí)行完\color{blue}{Block 0}。但要想執(zhí)行\color{green}{Block 1}糊识,又必須先執(zhí)行隊列里先進(jìn)入的\color{blue}{Block 0}(FIFO)绩社。\color{blue}{Block 0}\color{green}{Block 1}互相等待,所以造成了死鎖赂苗。

問題4:

以下代碼是在主線程執(zhí)行的愉耙,會不會產(chǎn)生死鎖?

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"執(zhí)行任務(wù)1");

  dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myqueue2", DISPATCH_QUEUE_CONCURRENT);
  dispatch_async(queue, ^{ // Block 0
    NSLog(@"執(zhí)行任務(wù)2");

    dispatch_sync(queue2, ^{  // Block 1
      NSLog(@"執(zhí)行任務(wù)3");
    });

    NSLog(@"執(zhí)行任務(wù)4");
  });
  NSLog(@"執(zhí)行任務(wù)5");
}

答案:不會拌滋!執(zhí)行順序是15234朴沿。
分析:因為\color{blue}{Block 0}\color{green}{Block 1}所處兩個不同隊列。就算這道題兩個都是串行隊列败砂,也不會產(chǎn)生死鎖赌渣。

問題5:

以下代碼是在主線程執(zhí)行的,會不會產(chǎn)生死鎖昌犹?

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"執(zhí)行任務(wù)1");

  dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
  dispatch_async(queue, ^{ // Block 0
    NSLog(@"執(zhí)行任務(wù)2");

    dispatch_sync(queue, ^{  // Block 1
      NSLog(@"執(zhí)行任務(wù)3");
    });
 
    NSLog(@"執(zhí)行任務(wù)4");
  });
  NSLog(@"執(zhí)行任務(wù)5");
}

答案:不會锡垄!執(zhí)行順序是15234。
分析:因為\color{blue}{Block 0}\color{green}{Block 1}處在并發(fā)隊列祭隔,并發(fā)隊列不會阻塞货岭。

  • 總結(jié):
    使用 sync 函數(shù)往\color{red}{當(dāng)前}\color{blue}{串行}隊列中添加任務(wù)路操,會卡住當(dāng)前的串行隊列(產(chǎn)生死鎖)
    死鎖 的產(chǎn)生兩個條件:
    1、是 sync 同步的千贯;
    2屯仗、往\color{red}{當(dāng)前}的\color{blue}{串行}隊列添加任務(wù);

6搔谴、多線程的安全隱患

  • 資源共享
    ? 1 塊資源可能會被多個線程共享魁袜,也就是\color{red}{多個線程可能會訪問同一塊資源}
    ? 比如多個線程訪問同一個對象、同一個變量敦第、同一個文件峰弹。

  • 當(dāng)多個線程訪問同一塊資源時,很容易引發(fā)\color{red}{數(shù)據(jù)錯亂和數(shù)據(jù)安全}問題

7芜果、面試題

面試題1:

請問下面代碼的打印結(jié)果是什么鞠呈?


打印結(jié)果是:1、3
原因:
1右钾、performSelector:withObject:afterDelay: 的本質(zhì)是往 Runloop 中添加定時器
2蚁吝、子線程默認(rèn)沒有啟動 Runloop
如果換成performSelector:withObject則打印結(jié)果為 132窘茁,這個不帶 Delay 的方法不是 runloop 的,相當(dāng)于直接調(diào)用脆烟。

runloop 的代碼是沒有開源的山林,所以看不到 afterDelay 的實現(xiàn),如果想了解邢羔,可以看 GNUstep驼抹。

  • GNUstep
    GNUstep 是 GNU 計劃的項目之一,它將 Cocoa 的 OC 庫重新開源實現(xiàn)了一遍张抄。
    源碼地址:http://www.gnustep.org/resources/downloads.php
    雖然 GNUstep 不是蘋果官方源碼砂蔽,但還是具有一定的參考價值。
面試題2:

請問下面代碼的打印結(jié)果是什么署惯?


打印結(jié)果是:1 崩潰
分析:在執(zhí)行 performSelector 的時候左驾,目標(biāo)線程已經(jīng)退出了,所以崩潰了极谊」钣遥可以在block中開啟runloop:

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

這樣就會打印 12 了。因為里面啟動了一個 runloop轻猖,執(zhí)行完打印 1 以后線程并沒有死帆吻,而是 runloop 休眠了,之后 performSelector 喚醒runloop去執(zhí)行test咙边。

以上的總結(jié)參考了并部分摘抄了以下文章猜煮,非常感謝以下作者的分享4卧薄:
小馬哥-李明杰的《多線程》課程

轉(zhuǎn)載請備注原文出處,不得用于商業(yè)傳播——凡幾多

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末王带,一起剝皮案震驚了整個濱河市淑蔚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愕撰,老刑警劉巖刹衫,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異搞挣,居然都是意外死亡带迟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門囱桨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仓犬,“玉大人,你說我怎么就攤上這事蝇摸∩艏纾” “怎么了办陷?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵貌夕,是天一觀的道長。 經(jīng)常有香客問我民镜,道長啡专,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任制圈,我火速辦了婚禮们童,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鲸鹦。我一直安慰自己慧库,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布馋嗜。 她就那樣靜靜地躺著齐板,像睡著了一般。 火紅的嫁衣襯著肌膚如雪葛菇。 梳的紋絲不亂的頭發(fā)上甘磨,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音眯停,去河邊找鬼济舆。 笑死,一個胖子當(dāng)著我的面吹牛莺债,可吹牛的內(nèi)容都是我干的滋觉。 我是一名探鬼主播签夭,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼椎侠!你這毒婦竟也來了覆致?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤肺蔚,失蹤者是張志新(化名)和其女友劉穎煌妈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宣羊,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡璧诵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仇冯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片之宿。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖苛坚,靈堂內(nèi)的尸體忽然破棺而出比被,到底是詐尸還是另有隱情,我是刑警寧澤泼舱,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布等缀,位于F島的核電站,受9級特大地震影響娇昙,放射性物質(zhì)發(fā)生泄漏尺迂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一冒掌、第九天 我趴在偏房一處隱蔽的房頂上張望噪裕。 院中可真熱鬧,春花似錦股毫、人聲如沸膳音。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祭陷。三九已至,卻和暖如春氧急,著一層夾襖步出監(jiān)牢的瞬間颗胡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工吩坝, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留毒姨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓钉寝,卻偏偏與公主長得像弧呐,于是被迫代替她去往敵國和親闸迷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 了解多線程俘枫,首先我們需要了解以下知識 進(jìn)程 ●進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序腥沽,就是一段程序的執(zhí)行過程,我們...
    趙哥窟閱讀 101評論 0 0
  • 前言:本文章摘自作者devsongxx,鏈接:http://www.reibang.com/p/8ff1eaeb...
    賈小敏1234閱讀 499評論 0 0
  • 目錄 簡述 NSThread GCD操作與隊列異步操作并行隊列同步操作并行隊列同步操作串行隊列異步操作串行隊列隊列...
    魚王00閱讀 502評論 0 2
  • 用了這么久的GCD, 不總結(jié)一下實在良心上過不去. 有那么點白那啥的意思. 廢話不多說. 走你 ? 1 GCD介紹...
    lb_閱讀 715評論 2 4
  • 多線程 優(yōu)缺點鸠蚪,實際應(yīng)用多線程比較 死鎖:向同一個/當(dāng)前的串行隊列添加同步sync操作任務(wù)今阳,會產(chǎn)生死鎖,新等舊茅信,舊...
    憤斗的小螞蟻閱讀 1,022評論 0 4