死鎖 GCD 多線程 Ios


IosGCD死鎖困擾很多人,分享一點個人經驗,希望可以幫助到更多人.文章有點長,首先第一張圖是正確的代碼,交代一下基本流程和原理,第二張圖是一個最簡單的死鎖后面是原理分析,第三張圖稍加了一點點難度的死鎖,后面是原理分析,第四章是正確的代碼,后面是原理分析

我在后面又補充了一篇文章來說死鎖.<死鎖補充>

.首先來看這段 正確的  代碼:

touchesbegan中調用test方法,可以看到如下打印

 分析一下原理:

1>調用test方法執,行到25行時沒有開啟線程,在主線程中打印

2> 執行到31行時檢測到異步函數async,此時async函數直接返回,不會等函數中的block1執行完畢就返回,執行到31行系統有兩件事要做,第一跳至42行打印,第二開啟一個新的線程1(是子線程而非主線程)執行block1當中的內容.所以看到主線程0,4先打印,打印結束就沒有主線程的事情了,接下來子線程登場.()

3> 子線程1執行block1的內容:

     3.1執行33行打印    (子線程1)  打印

     3.2執行到35行檢測到async異步函數,直接返回,不等待block2執行完畢,程序繼續向下執行,即打印39 3 子線程打印,同時又創建一個  (子線程2)

4> ok  到這里我們還需要分析一下,程序運行到這里我們一共經歷了幾個線程?三個!主線程一個,31 35行分別創建了兩個子線程12

(怎么看是否創建了新的線程?"一般"來說看函數,async異步執行還是sync同步執行,為什么要用"一般"?往下看)

     那么問題來了1>   33行和 39行的打印(1,3打印)都在一個線程上我們可以理解(看線程地址),但是為什么36行的打印 也和這兩個            同一個線程呢?  因為31行創建了一個子線程 打印了33行和39行之后,這個線程的任務結束了,正常來說就要銷毀了,沒他什么事了.但是我們在35行又需要創建一個線程,創建線程需要時間和空間成本(占用內存)所以在子線程1執行完任務進入線程池要銷毀的時候,發現系統還要再創建一個線程,ok那我就不銷毀了,你也不用再創建了,所以線程1就被重復利用了,所以他們在一個線程上打印.關於為什么在同一個子線程上打印,我給出的只是一個理解的方式,是不是准確的?我不確定.因為線程很多的情況十分復雜,我們這個事例程序很簡單,可以這樣理解.所以我前面說的是有問題的,這個程序只經歷了兩個線程,主線程和同一個子線程

5>ok我們回來  在子線程1執行33  39行的打印之后,被系統重復利用,來執行35block的內容,36行打印結束一個消息循環結束,進入休眠等待下次觸摸屏幕.

6>總結:為什么程序的打印結果會是這樣的?與兩個因素有關1>函數  async異步執行還是sync同步執行2>隊列我們創建的是串行隊列這個串行隊列里面有兩個任務 blockblock2,串行隊列遵循fifo原則:first  in  first out,就先進先出原則,blokc131行加入,先執行,block235行加入后執行.block1中有三個任務3339行打印和block2,block2在兩個打印中間,按照程序自上到下執行尼瑪問題又來了,為什么block2不是在兩個打印任務之間執行?而是在打印結束后執行?ok 這時就要看函數啦~async 這是什么?異步執行~!!要開啟新的線程(或是重復利用線程池中已經創建的線程,什么樣的線程可以重復利用?這個線程已經執行完它的任務,進入線程池馬上銷毀的線程才可以重復利用)(開啟線程或是獲取線程池中重復利用的線程是需要時間成本的)檢測到這個函數怎么辦?直接返回,不用等這個函數的block執行完就返回.什么意思?執行到35行的時候兵分兩路,一路向下執行39行打印,一路從線程池中獲取重復利用的線程處理block2的任務.在兵分兩路的時候子線程以還沒有處理完39行的打印,它的任務沒有結束啊!為什么處理block2沒有創建新的線程?因為需要時間和控件成本.GCD為我們做了優化,(怎么優化的我們不用操心~~看不到源碼這是個迷,不過看到了也不一定看得懂!哈哈所以不創建新的線程,等子線程1執行完他的任務,我們重復利用它!ok說完了,

.了解了上圖,我們再來看下面的代碼

我把35行的函數換成了sync同步執行函數,看打印結果,2.3沒有打印!這就是傳說中的死鎖.什么意思?36 ,39行沒有打印,程序在等待,卡住了.不能正常向下執行了!

分析一下原因:

1>點擊屏幕調用test方法,主線程打印2542,主線程不用我們創建.系統自動創建.上面已經提到原理就不再贅述,直接來重點

2>31行代碼做了這幾件事:1block1任務添加到隊列串行queue,2>利用async函數開啟新的子線程1處理block1任務.

3>當執行到35行代碼時做了這幾件事情:1>block2添加到串行隊列queue,2>sync函數不會開啟新的線程,只能在當前線程執行任務

4>ok現在我們分析一下是什么情況:1>queue當中有兩個任務,block1block2.2>只有一個線程:子線程1,31行創建的.queue串行執行任務,要等block1執行完才能執行block2任務.啥意思?31行創建的子線程1要執行完block1才能執行block2.執行到35block2任務的時候要等待block1執行完,可是程序自上而下執行,尼瑪block2不執行完block1就不會執行完!你懂了么?block2所在的是sync函數不能開啟新的線程,只能和block1共用一個線程,這個線程在處理block1,現在的問題是沒有線程處理block2的任務~!!!所以就出現了傳說中的死鎖!別尼瑪問我為啥主線程不處理block2的任務!我會讓你的老板開除你的!還沒懂?最后說一遍:程序自上而下執行知道吧?那么block1執行完時不時需要block1里面的所有代碼都執行完啊?可是到了35block2沒有開啟新的線程,只能和block1共用一個線程,現在這個線程在處理block1的任務,要等block1執行完才能執行block2的任務.所以程序就卡住了.ok還不懂?看上面正確的代碼第35行我們呢用的是async函數,它能開啟一個全新的線程來處理block2任務,且不用等到block2執行完這個函數就返回了!函數返回啥意思----就是繼續向下執行了.ok說完了.別告訴我你還沒

:有了上面的分析相信小伙伴們有了一定的了解,再來分析一下下面的代碼是不是死鎖?

所在哪里了?

是死鎖,鎖在64行的代碼.

分析一下原因:

1>主線程執行47, 71行打印之后進入 休眠狀態等待下次點擊屏幕,在兩個打印之間檢測到54async不等block1執行完繼續向下.ok沒問題

2>54行將block1加入到queue隊列,async函數開啟新的線程執行block1,block1任務代碼書訊執行  56行打印

3>58行檢測到async函數不等block2執行完繼續向下執行,62行打印,

4>執行到64 sync函數 沒有開啟新的線程,要和block1共用一個線程,造成死鎖

5>分析一下現在什么情況:queue中有3個任務,block1,block2,block3,順序執行,為什么卡在64?不是58?58行開啟了一個新的線程來處理block2,所以不是這里.block3沒有開啟線程要用別人的線程,用誰的呢?你看它在哪個block.她在block1里面,block154行開啟了線程正在處理block1,所以現在block2沒人處理.就造成死鎖.

6>那么為什么block2沒有打印呢?有線程處理他的任務啊?這是要看隊列queue,三個任務順序執行,block1執行完了么?沒有,所以block2不會打印.ok說完了!有疑問?block2?好吧 來看下面代碼.我把64行的函數換了.

為什么是這個打印順序呀?

1>主線程優先處理,所以 0,6先打印,

2>為什么135這個順序打印丫?135在同一個線程中,block1的線程中,代碼順序執行,所以這個順序

3>為什么2,4打印不是穿插在1,3,5之間呢?1>因為2打印要開啟新的線程,有時間成本,啥意思,就是我開啟一個線程需要時間.所以他要晚

                                                                               2>135在主線程,優先於子線程處理任務,優先級就比子線程高,所以它晚

                                                                               3>async函數不等待后面block執行完就向下執行,所以它晚,

4>為什么2,4打印沒有開啟兩個線程而是一個線程,因為開啟新的線程需要時間和控件成本GCD為我們自動做了優化,重復利用了線程池中執行完本職任務即將銷毀的線程.

5>為什么24的打印順序是這樣:1>因為4利用了2的線程,(看線程地址)代碼自上而下執行,queueblock2排在block3之前,所以要block2打印完才能打印block3.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM