我們在面試的時候經常會遇到很多關于多執行緒的面試題,這些面試題的答案你都知道嗎?
1.你理解的多執行緒是什么?
2.iOS的多執行緒方案有哪些?你更傾向于哪一種?為什么?
3.你在專案中用過GCD嗎?有哪些使用?
4.GCD的佇列型別有哪些?
5.什么情況下會產生死鎖?
6.說一下OperationQueue和GCD的區別,以及各自的優勢是什么?
7.執行緒安全的處理手段有哪些?
8.OC你了解的鎖有哪些?在你回答的基礎上進行二次提問;
追問一:自旋鎖和互斥鎖對比?
追問二:使用以上鎖需要注意哪些?
追問三:用C/OC/C++,任選其一,實作自旋或互斥鎖,口訴即可.
.....
相信以上的前面的幾個問題,大家都是很容易回答出來的,但是后面的關于鎖的問題,我們可能用得比較少,比如加鎖,解鎖、自旋鎖、互斥鎖等等,今天就讓我們一起了解這些東西,面試官問到多執行緒,那就是隨便問,我們也能隨便答.首先我們簡單來回顧一下基礎的知識.
iOS多執行緒有哪些?
pthread:是一個通用的多執行緒API,適用于Unix\Linux\Windows等多個系統,可以跨平臺,但是由于它的語言是C語言,而且記憶體是我們程式員自己管理,所以使用難度比較大,而且幾乎是不用的.
NSThread:使用起來更加面向物件,簡單易用,可以直接操作執行緒物件,它的使用語言是OC,它的記憶體也是由我們程式員自己管理,所以偶爾使用
GCD:旨在替換NSThread等執行緒技術,它的一個優點可以充分利用設備的多核,它的語言是C語言,執行緒的生命周期是自動管理,所以用起來比較方便,所以經常被使用
NSOperation:它是基于GCD(底層是GCD),它比GCD多了一些更簡單實用的功能,使用起來更加面向物件,它的語言也是OC,執行緒的生命周期也是自動管理,所以用起來也比較方便,也是經常被使用
下面用一張圖,放在一起對比,看著比較明顯和好記:

再補充一點:NSThread、GCD、NSOperation底層都是基于pthread.
我們用得比較多的是用GCD,我自己也是喜歡用GCD.下面就來介紹一下
GCD的常用函式有哪些?
1.用同步的方式來執行任務:
dispatch_sync(queue, block); queue:佇列, block:任務.
2.用異步的方式來執行任務:
dispatch_async(queue, block); queue:佇列, block:任務.

number=1是代表主執行緒,number=不是1就是子執行緒.下面是原始碼下載地址(網路不好的可能要開vpn才能打開)
GCD原始碼下載地址
GCD的佇列
GCD的佇列可以分為2大型別
1.并發佇列 (Concurrent Dispatch Queue)
可以讓多個任務并發 (同時) 執行 (自動開啟多個執行緒同時執行任務)
并發功能只有在異步 (dispatch_async)函式下才有效
2.串行佇列 ( Serial Dispatch Queue)
讓任務一個接一個地執行 (一個任務執行完畢后,再執行下一個任務)
同步、異步、并發、串行 4個術語的區分
同步和異步主要影響:能不能開啟新的執行緒
同步:在當前執行緒中執行任務,不具備開啟新執行緒的能力
異步:在新的執行緒中執行任務,具備開啟新執行緒的能力
并發和串行主要影響:任務的執行方式
并發:多個任務并發(同時)執行
串行:一個任務執行完畢后,再執行下一個任務
有個小提問:
dispatch_async(queue, block)在執行任務的時候,一定開啟新的執行緒嗎?
答案是否定的,因為我們知道有個特殊的串行佇列,就是主佇列,只要是主佇列,不管你是同步還是異步執行緒,都是在當前執行緒執行,不會開啟新的執行緒,請看下圖:

這樣的話就有同步、異步情況下有手動創建的串行、并發、主佇列組合6種情況.
請看下圖:

接下來,我們就說一下,面試題中常見的執行緒死鎖問題.
什么情況下會產生死鎖?
所謂死鎖就意味著卡住了,這個執行緒不會往下執行了.請問以下代碼會不會產生死鎖:
(案例1我會仔細解釋,后面的死鎖案例就不詳細解釋了,道理都是一樣的,下面一句話總結)
案例一:

請問上面的案例會不會出現死鎖?答案是會的,首先佇列的特點是:FIFO,first in first out,先進先出.因為我們知道dispatch_sync是同步方法,它不會開啟新的執行緒,所以它肯定也是在主執行緒執行的,所以任務1執行完了,肯定執行任務2,而任務3在等任務2執行完畢;
同樣的queue是主佇列,而任務2是放在主佇列里面的,而任務2主佇列前面是viewDidLoad方法,所以任務2會等viewDidLoad執行完,也就是任務2會等任務3執行完才會執行,那就很明顯了著2個任務相互等待造成死鎖!
我們看一下運行結果:

直接崩潰.如果用畫圖來解釋一下,也是比較清晰,請看下圖

所以看圖就很清晰,想取出任務2必須viewDidLoad執行完畢,viewDidLoad執行完畢必須任務3執行完畢,而任務3執行完畢,必須要執行完sync,而sync又取不出來任務2,所以造成死鎖.我們也可以證明任務2只要是主佇列中,它肯定是在viewDidLoad后面執行.我們只要把上面的同步改成異步即可,請看下圖:

案例二:

這個會不會死鎖?答案是會死鎖.首先block0和block1都是在同一個queue中,block1在后面,而block1是同步的dispatch_sync所以會等block0執行完,再執行block1,所以是任務3等任務4;而dispatch_sync是同步的,任務4又會等任務3執行完!相互等待,死鎖!
看一下執行結果:

案例三:

不會死鎖
案例四:

不會死鎖
從上面的例子,大家是不是覺得:只要是同步的,而且往同一個佇列加任務就會死鎖呢?
那么請看下面的案例
案例五:

當前就是同步的,而且往同一個佇列加任務!此時是不會產生死鎖.佇列必須也是串行佇列才是死鎖!
所以通過以上的案例,我們得出以下總結,只要記住這句話,我們就知道什么情況下會出現死鎖.
死鎖總結:
使用sync函式往當前串行佇列中添加任務
會卡住當前的串行佇列(產生死鎖)
注意條件:
1.sync函式
2.當前串行佇列:同一個佇列而且是串行佇列
知道這個結論我們就可以知道什么時候產生死鎖!辨別死鎖就非常簡單,上面的案例都適用,大家可以自己看下.
佇列組的使用
比如我們現在有一個需求是:異步并發執行任務1、任務2;等任務1、任務2都執行完畢后,再回到主執行緒執行任務3,這個時候我們就用到GCD的一個佇列組的應用.

這里任務3也可以精簡寫,直接把queue改成main_queue就可以;還有個就是我們還可以添加任務4,也是和任務3一樣的寫法,那么任務3和任務4就會等任務1和任務2執行完以后,交替執行!
由于多執行緒內容較多,我會在接下來的博客繼續介紹!(如:多執行緒的其他面試題、多執行緒所有的鎖對比及使用等).
接下來我會繼續努力撰寫其他博客,您的支持就是我最大的動力!
如果覺得我寫得對您有所幫助,請點贊關注我,我會持續更新😄
感謝支持🙏🙏🙏!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/278810.html
標籤:其他
上一篇:CSRF:你的身邊安全嗎
