轉(zhuǎn)載的一位大牛的GCD死鎖分析感覺很不錯(cuò),在此分享(http://www.cnblogs.com/LDSmallCat/p/4910080.html)
一.首先來看這段 正確的 代碼:
在touchesbegan中調(diào)用test方法,可以看到如下打印
分析一下原理:
1>調(diào)用test方法執(zhí),行到25行時(shí)沒有開啟線程,在主線程中打印
2> 執(zhí)行到31行時(shí)檢測(cè)到異步函數(shù)async,此時(shí)async函數(shù)直接返回,不會(huì)等函數(shù)中的block1執(zhí)行完畢就返回,執(zhí)行到31行系統(tǒng)有兩件事要做,第一跳至42行打印,第二開啟一個(gè)新的線程1(是子線程而非主線程)執(zhí)行block1當(dāng)中的內(nèi)容.所以看到主線程0,4先打印,打印結(jié)束就沒有主線程的事情了,接下來子線程登場(chǎng).()
3> 子線程1執(zhí)行block1的內(nèi)容:
3.1執(zhí)行33行打印 在(子線程1) 打印
3.2執(zhí)行到35行檢測(cè)到async異步函數(shù),直接返回,不等待block2執(zhí)行完畢,程序繼續(xù)向下執(zhí)行,即打印39行 3 子線程打印,同時(shí)又創(chuàng)建一個(gè) (子線程2)
4> ok 到這里我們還需要分析一下,程序運(yùn)行到這里我們一共經(jīng)歷了幾個(gè)線程?三個(gè)!主線程一個(gè),31行 35行分別創(chuàng)建了兩個(gè)子線程1和2
(怎么看是否創(chuàng)建了新的線程?"一般"來說看函數(shù),是async異步執(zhí)行還是sync同步執(zhí)行,為什么要用"一般"?往下看)
那么問題來了1> 33行和 39行的打印(即1,3打印)都在一個(gè)線程上我們可以理解(看線程地址),但是為什么36行的打印 也和這兩個(gè) 在 同一個(gè)線程呢? 因?yàn)?1行創(chuàng)建了一個(gè)子線程 打印了33行和39行之后,這個(gè)線程的任務(wù)結(jié)束了,正常來說就要銷毀了,沒他什么事了.但是我們?cè)?5行又需要?jiǎng)?chuàng)建一個(gè)線程,創(chuàng)建線程需要時(shí)間和空間成本(占用內(nèi)存)所以在子線程1執(zhí)行完任務(wù)進(jìn)入線程池要銷毀的時(shí)候,發(fā)現(xiàn)系統(tǒng)還要再創(chuàng)建一個(gè)線程,ok那我就不銷毀了,你也不用再創(chuàng)建了,所以線程1就被重復(fù)利用了,所以他們?cè)谝粋€(gè)線程上打印.關(guān)于為什么在同一個(gè)子線程上打印,我給出的只是一個(gè)理解的方式,是不是準(zhǔn)確的?我不確定.因?yàn)榫€程很多的情況十分復(fù)雜,我們這個(gè)事例程序很簡(jiǎn)單,可以這樣理解.所以我前面說的是有問題的,這個(gè)程序只經(jīng)歷了兩個(gè)線程,主線程和同一個(gè)子線程
5>ok我們回來 在子線程1執(zhí)行33 39行的打印之后,被系統(tǒng)重復(fù)利用,來執(zhí)行35行block的內(nèi)容,36行打印結(jié)束一個(gè)消息循環(huán)結(jié)束,進(jìn)入休眠等待下次觸摸屏幕.
6>總結(jié):為什么程序的打印結(jié)果會(huì)是這樣的?與兩個(gè)因素有關(guān)1>函數(shù) 是async異步執(zhí)行還是sync同步執(zhí)行2>隊(duì)列我們創(chuàng)建的是串行隊(duì)列這個(gè)串行隊(duì)列里面有兩個(gè)任務(wù) block1 和block2,串行隊(duì)列遵循fifo原則:first in first out,就先進(jìn)先出原則,blokc1在31行加入,先執(zhí)行,block2在35行加入后執(zhí)行.block1中有三個(gè)任務(wù)33行39行打印和block2,且block2在兩個(gè)打印中間,按照程序自上到下執(zhí)行…尼瑪問題又來了,為什么block2不是在兩個(gè)打印任務(wù)之間執(zhí)行?而是在打印結(jié)束后執(zhí)行?ok 這時(shí)就要看函數(shù)啦~async 這是什么?異步執(zhí)行!!要開啟新的線程(或是重復(fù)利用線程池中已經(jīng)創(chuàng)建的線程,什么樣的線程可以重復(fù)利用?這個(gè)線程已經(jīng)執(zhí)行完它的任務(wù),進(jìn)入線程池馬上銷毀的線程才可以重復(fù)利用)(開啟線程或是獲取線程池中重復(fù)利用的線程是需要時(shí)間成本的)檢測(cè)到這個(gè)函數(shù)怎么辦?直接返回,不用等這個(gè)函數(shù)的block執(zhí)行完就返回.什么意思?執(zhí)行到35行的時(shí)候兵分兩路,一路向下執(zhí)行39行打印,一路從線程池中獲取重復(fù)利用的線程處理block2的任務(wù).在兵分兩路的時(shí)候子線程以還沒有處理完39行的打印,它的任務(wù)沒有結(jié)束啊!為什么處理block2沒有創(chuàng)建新的線程?因?yàn)樾枰獣r(shí)間和控件成本.GCD為我們做了優(yōu)化,(怎么優(yōu)化的我們不用操心~看不到源碼這是個(gè)迷,不過看到了也不一定看得懂!哈哈所以不創(chuàng)建新的線程,等子線程1執(zhí)行完他的任務(wù),我們重復(fù)利用它!ok說完了,
二.了解了上圖,我們?cè)賮砜聪旅娴拇a
我把35行的函數(shù)換成了sync同步執(zhí)行函數(shù),看打印結(jié)果,2.3沒有打印!這就是傳說中的死鎖.什么意思?36 ,39行沒有打印,程序在等待,卡住了.不能正常向下執(zhí)行了!
分析一下原因:
1>點(diǎn)擊屏幕調(diào)用test方法,主線程打印25行42行,主線程不用我們創(chuàng)建.系統(tǒng)自動(dòng)創(chuàng)建.上面已經(jīng)提到原理就不再贅述,直接來重點(diǎn)
2>31行代碼做了這幾件事:1將block1任務(wù)添加到隊(duì)列串行queue中,2>利用async函數(shù)開啟新的子線程1處理block1任務(wù).
3>當(dāng)執(zhí)行到35行代碼時(shí)做了這幾件事情:1>將block2添加到串行隊(duì)列queue中,2>sync函數(shù)不會(huì)開啟新的線程,只能在當(dāng)前線程執(zhí)行任務(wù)
4>ok現(xiàn)在我們分析一下是什么情況:1>queue當(dāng)中有兩個(gè)任務(wù),block1和block2.2>只有一個(gè)線程:子線程1,在31行創(chuàng)建的.queue串行執(zhí)行任務(wù),要等block1執(zhí)行完才能執(zhí)行block2任務(wù).啥意思?31行創(chuàng)建的子線程1要執(zhí)行完block1才能執(zhí)行block2.執(zhí)行到35行block2任務(wù)的時(shí)候要等待block1執(zhí)行完,可是程序自上而下執(zhí)行,尼瑪block2不執(zhí)行完block1就不會(huì)執(zhí)行完!你懂了么?block2所在的是sync函數(shù)不能開啟新的線程,只能和block1共用一個(gè)線程,這個(gè)線程在處理block1,現(xiàn)在的問題是沒有線程處理block2的任務(wù)~!!!所以就出現(xiàn)了傳說中的死鎖!別尼瑪問我為啥主線程不處理block2的任務(wù)!我會(huì)讓你的老板開除你的!還沒懂?最后說一遍:程序自上而下執(zhí)行知道吧?那么block1執(zhí)行完時(shí)不時(shí)需要block1里面的所有代碼都執(zhí)行完啊?可是到了35行block2沒有開啟新的線程,只能和block1共用一個(gè)線程,現(xiàn)在這個(gè)線程在處理block1的任務(wù),要等block1執(zhí)行完才能執(zhí)行block2的任務(wù).所以程序就卡住了.ok還不懂?看上面正確的代碼第35行我們呢用的是async函數(shù),它能開啟一個(gè)全新的線程來處理block2任務(wù),且不用等到block2執(zhí)行完這個(gè)函數(shù)就返回了!函數(shù)返回啥意思----就是繼續(xù)向下執(zhí)行了.ok說完了.別告訴我你還沒
三:有了上面的分析相信小伙伴們有了一定的了解,再來分析一下下面的代碼是不是死鎖?
所在哪里了?
死鎖,鎖在64行的代碼.
分析一下原因:
1>主線程執(zhí)行47, 71行打印之后進(jìn)入 休眠狀態(tài)等待下次點(diǎn)擊屏幕,在兩個(gè)打印之間檢測(cè)到54行async不等block1執(zhí)行完繼續(xù)向下.ok沒問題
2>54行將block1加入到queue隊(duì)列,開async函數(shù)開啟新的線程執(zhí)行block1,block1任務(wù)代碼書訊執(zhí)行 56行打印
3>58行檢測(cè)到async函數(shù)不等block2執(zhí)行完繼續(xù)向下執(zhí)行,62行打印,
4>執(zhí)行到64行 sync函數(shù) 沒有開啟新的線程,要和block1共用一個(gè)線程,造成死鎖
5>分析一下現(xiàn)在什么情況:queue中有3個(gè)任務(wù),block1,block2,block3,順序執(zhí)行,為什么卡在64行?不是58行?58行開啟了一個(gè)新的線程來處理block2,所以不是這里.block3沒有開啟線程要用別人的線程,用誰的呢?你看它在哪個(gè)block里.她在block1里面,block1在54行開啟了線程正在處理block1,所以現(xiàn)在block2沒人處理.就造成死鎖.
6>那么為什么block2沒有打印呢?有線程處理他的任務(wù)啊?這是要看隊(duì)列queue,三個(gè)任務(wù)順序執(zhí)行,block1執(zhí)行完了么?沒有,所以block2不會(huì)打印.ok說完了!有疑問?block2?好吧 來看下面代碼.我把64行的函數(shù)換了.
為什么是這個(gè)打印順序呀?
1>主線程優(yōu)先處理,所以 0,6先打印,
2>為什么135這個(gè)順序打印丫?135在同一個(gè)線程中,即block1的線程中,代碼順序執(zhí)行,所以這個(gè)順序
3>為什么2,4打印不是穿插在1,3,5之間呢?1>因?yàn)?打印要開啟新的線程,有時(shí)間成本,啥意思,就是我開啟一個(gè)線程需要時(shí)間.所以他要晚
2>135在主線程,優(yōu)先于子線程處理任務(wù),優(yōu)先級(jí)就比子線程高,所以它晚
3>async函數(shù)不等待后面block執(zhí)行完就向下執(zhí)行,所以它晚,
4>為什么2,4打印沒有開啟兩個(gè)線程而是一個(gè)線程,因?yàn)殚_啟新的線程需要時(shí)間和控件成本GCD為我們自動(dòng)做了優(yōu)化,重復(fù)利用了線程池中執(zhí)行完本職任務(wù)即將銷毀的線程.
5>為什么24的打印順序是這樣:1>因?yàn)?利用了2的線程,(看線程地址)代碼自上而下執(zhí)行,queue中block2排在block3之前,所以要block2打印完才能打印block3.