我們在面試的時候經常會遇到很多關于多線程的面試題,這些面試題的答案你都知道嗎?
1.你理解的多線程是什么?
2.iOS的多線程方案有哪些?你更傾向于哪一種?為什么?
3.你在項目中用過GCD嗎?有哪些使用?
4.GCD的隊列類型有哪些?
5.什么情況下會產生死鎖?
6.說一下OperationQueue和GCD的區(qū)別,以及各自的優(yōu)勢是什么?
7.線程安全的處理手段有哪些?
8.OC你了解的鎖有哪些?在你回答的基礎上進行二次提問;
追問一:自旋鎖和互斥鎖對比?
追問二:使用以上鎖需要注意哪些?
追問三:用C/OC/C++,任選其一,實現自旋或互斥鎖,口訴即可.
.....
相信以上的前面的幾個問題,大家都是很容易回答出來的,但是后面的關于鎖的問題,我們可能用得比較少,比如加鎖,解鎖揭措、自旋鎖、互斥鎖等等,今天就讓我們一起了解這些東西,面試官問到多線程,那就是隨便問,我們也能隨便答.首先我們簡單來回顧一下基礎的知識.
iOS多線程有哪些?
pthread:是一個通用的多線程API,適用于Unix\Linux\Windows等多個系統(tǒng),可以跨平臺,但是由于它的語言是C語言,而且內存是我們程序員自己管理,所以使用難度比較大,而且?guī)缀跏遣挥玫?
NSThread:使用起來更加面向對象,簡單易用,可以直接操作線程對象,它的使用語言是OC,它的內存也是由我們程序員自己管理,所以偶爾使用
GCD:旨在替換NSThread等線程技術,它的一個優(yōu)點可以充分利用設備的多核,它的語言是C語言,線程的生命周期是自動管理,所以用起來比較方便,所以經常被使用
NSOperation:它是基于GCD(底層是GCD),它比GCD多了一些更簡單實用的功能,使用起來更加面向對象,它的語言也是OC,線程的生命周期也是自動管理,所以用起來也比較方便,也是經常被使用
下面用一張圖,放在一起對比,看著比較明顯和好記:
再補充一點:NSThread、GCD、NSOperation底層都是基于pthread.
我們用得比較多的是用GCD,我自己也是喜歡用GCD.下面就來介紹一下
GCD的常用函數有哪些?
1.用同步的方式來執(zhí)行任務:
dispatch_sync(queue, block); ? ?queue:隊列, ?block:任務.
2.用異步的方式來執(zhí)行任務:
dispatch_async(queue, block); ? ?queue:隊列, ?block:任務.
number=1是代表主線程,number=不是1就是子線程.下面是源碼下載地址(網絡不好的可能要開vpn才能打開)
GCD的隊列
GCD的隊列可以分為2大類型
1.并發(fā)隊列 (Concurrent Dispatch Queue)
可以讓多個任務并發(fā)?(同時) 執(zhí)行 (自動開啟多個線程同時執(zhí)行任務)
并發(fā)功能只有在異步 (dispatch_async)函數下才有效
2.串行隊列 ( Serial Dispatch Queue)
讓任務一個接一個地執(zhí)行 (一個任務執(zhí)行完畢后,再執(zhí)行下一個任務)
同步罢猪、異步收苏、并發(fā)、串行 4個術語的區(qū)分
同步和異步主要影響:能不能開啟新的線程
同步:在當前線程中執(zhí)行任務,不具備開啟新線程的能力
異步:在新的線程中執(zhí)行任務,具備開啟新線程的能力
并發(fā)和串行主要影響:任務的執(zhí)行方式
并發(fā):多個任務并發(fā)(同時)執(zhí)行
串行:一個任務執(zhí)行完畢后,再執(zhí)行下一個任務
有個小提問:
dispatch_async(queue, block)在執(zhí)行任務的時候,一定開啟新的線程嗎?
答案是否定的,因為我們知道有個特殊的串行隊列,就是主隊列,只要是主隊列,不管你是同步還是異步線程,都是在當前線程執(zhí)行,不會開啟新的線程,請看下圖:
這樣的話就有同步生百、異步情況下有手動創(chuàng)建的串行孙蒙、并發(fā)项棠、主隊列組合6種情況.
請看下圖:
接下來,我們就說一下,面試題中常見的線程死鎖問題.
什么情況下會產生死鎖?
所謂死鎖就意味著卡住了,這個線程不會往下執(zhí)行了.請問以下代碼會不會產生死鎖:
(案例1我會仔細解釋,后面的死鎖案例就不詳細解釋了,道理都是一樣的,下面一句話總結)
案例一:
請問上面的案例會不會出現死鎖?答案是會的,首先隊列的特點是:FIFO,first in first out,先進先出.因為我們知道dispatch_sync是同步方法,它不會開啟新的線程,所以它肯定也是在主線程執(zhí)行的,所以任務1執(zhí)行完了,肯定執(zhí)行任務2,而任務3在等任務2執(zhí)行完畢;
同樣的queue是主隊列,而任務2是放在主隊列里面的,而任務2主隊列前面是viewDidLoad方法,所以任務2會等viewDidLoad執(zhí)行完,也就是任務2會等任務3執(zhí)行完才會執(zhí)行,那就很明顯了著2個任務相互等待造成死鎖!
我們看一下運行結果:
直接崩潰.如果用畫圖來解釋一下,也是比較清晰,請看下圖
所以看圖就很清晰,想取出任務2必須viewDidLoad執(zhí)行完畢,viewDidLoad執(zhí)行完畢必須任務3執(zhí)行完畢,而任務3執(zhí)行完畢,必須要執(zhí)行完sync,而sync又取不出來任務2,所以造成死鎖.我們也可以證明任務2只要是主隊列中,它肯定是在viewDidLoad后面執(zhí)行.我們只要把上面的同步改成異步即可,請看下圖:
案例二:
這個會不會死鎖?答案是會死鎖.首先block0和block1都是在同一個queue中,block1在后面,而block1是同步的dispatch_sync所以會等block0執(zhí)行完,再執(zhí)行block1,所以是任務3等任務4;而dispatch_sync是同步的,任務4又會等任務3執(zhí)行完!相互等待,死鎖!
看一下執(zhí)行結果:
案例三:
不會死鎖
案例四:
不會死鎖
從上面的例子,大家是不是覺得:只要是同步的,而且往同一個隊列加任務就會死鎖呢?
那么請看下面的案例
案例五:
當前就是同步的,而且往同一個隊列加任務!此時是不會產生死鎖.隊列必須也是串行隊列才是死鎖!
所以通過以上的案例,我們得出以下總結,只要記住這句話,我們就知道什么情況下會出現死鎖.
死鎖總結:
使用sync函數往當前串行隊列中添加任務
會卡住當前的串行隊列(產生死鎖)
注意條件:
1.sync函數
2.當前串行隊列:同一個隊列而且是串行隊列
知道這個結論我們就可以知道什么時候產生死鎖!辨別死鎖就非常簡單,上面的案例都適用,大家可以自己看下.
隊列組的使用
比如我們現在有一個需求是:異步并發(fā)執(zhí)行任務1、任務2;等任務1挎峦、任務2都執(zhí)行完畢后,再回到主線程執(zhí)行任務3,這個時候我們就用到GCD的一個隊列組的應用.
這里任務3也可以精簡寫,直接把queue改成main_queue就可以;還有個就是我們還可以添加任務4,也是和任務3一樣的寫法,那么任務3和任務4就會等任務1和任務2執(zhí)行完以后,交替執(zhí)行!
由于多線程內容較多,我會在接下來的博客繼續(xù)介紹!(如:多線程的其他面試題香追、多線程所有的鎖對比及使用等).