iOS執行緒死鎖分析
阿新 • • 發佈:2019-02-11
iOS執行緒死鎖
在chat view的開發過程中,添加了“混合標籤新增與顯示”,app出現傳送圖片會出現卡死的情況,但過了大約30~40 second後會恢復正常。
問題分析:
因為沒有任何報錯與提示,只能根據表面現象慢慢分析,經過多次測試與觀察得出以下規律:
(1)傳送表情與文字不會發生該情況,只有傳送圖片才會發生app介面卡死的情況。(主執行緒阻塞,與大檔案上傳有關)
(2)app卡死一定時間後會恢復正常,但時間不定,大約範圍在30~40 second。(主執行緒解除阻塞,與系統某些機制有關)
(3)當介面中有gif圖時才會發生,介面中全是移動端本地圖片是能順利傳送。(與gif下載有關)
根據上述現象,可以總結為:主執行緒阻塞,過後因通訊通訊失敗而阻塞解除。
因為與gif圖的下載有關,於是分析gif的下載實現,gif圖的下載由以下程式碼實現。
其中,關於NSData的靜態方法dataWithContentsOfURL的說明中有以下使用說明。
這裡說明了要儘量避免使用dataWithContentsOfURL從網路下載資源,因為它會阻塞當前執行緒,若下載的檔案比較大而網路又不好就會帶來很差的使用者體驗,對於iOS裝置還可能引起app被迫終止。
雖然只需要把dataWithContentsOfURL放置到別的執行緒中實現,又或者使用NSURLConnect、NSURLSession中的非同步請求方法也能解決問題,但本質上引起這問題是因為呼叫dataWithContentsOfURL時網路環境差嗎?
明顯不是,因為測試環境是模擬器,模擬器的cpu、網路通訊是比真機要好的,只有GPU是不如真機。而且問題的出現規律是固定。
但dataWithContentsOfURL已經解析了為什麼主執行緒阻塞,那麼現在需要解析為什麼會阻塞那麼長的時間。
CPU分析結果:
圖中的標記的那段是發生異常情況時的CPU的情況。從CPU圖上可以更加肯定執行緒是並沒有死掉的,而且進入了無法響應的狀態,因此可以判斷出發生執行緒死鎖的情況。
執行緒死鎖
死鎖的概念:
死鎖是程序死鎖的簡稱,是由Dijkstra於1965年研究銀行家演算法時首先提出來的。它是計算機作業系統乃至併發程式設計中最難處理的問題之一。實際上,死鎖問題不僅在計算機系統中存在,在我們日常生活中它也廣泛存在。
我們先看看這樣一個生活中的例子:在一條河上有一座橋,橋面較窄,只能容納一輛汽車通過,無法讓兩輛汽車並行。如果有兩輛汽車A和B分別由橋的兩端駛上該橋,則對於A車來說,它走過橋面左面的一段路(即佔有了橋的一部分資源),要想過橋還須等待B車讓出右邊的橋面,此時A車不能前進;對於B車來說,它走過橋面右邊的一段路(即佔有了橋的一部分資源),要想過橋還須等待A車讓出左邊的橋面,此時B車也不能前進。兩邊的車都不倒車,結果造成互相等待對方讓出橋面,但是誰也不讓路,就會無休止地等下去。這種現象就是死鎖。如果把汽車比做程序,橋面作為資源,那麼上述問題就描述為:程序A佔有資源R1,等待程序B佔有的資源Rr;程序B佔有資源Rr,等待程序A佔有的資源R1。而且資源R1和Rr只允許一個程序佔用,即:不允許兩個程序同時佔用。結果,兩個程序都不能繼續執行,若不採取其它措施,這種迴圈等待狀況會無限期持續下去,就發生了程序死鎖。
上圖是一張描述一個簡單的死鎖模型的圖,首先Thread1鎖定佔有資源Y,Thread2鎖定佔有資源X,但同時相互向對方請求資源,因此都無法釋放自身資源去訪問下一個資源,結果兩個執行緒相互阻塞。
死鎖的四個充分必要條件
從以上分析可見,如果在計算機系統中同時具備下面四個必要條件時,那麼會發生死鎖。換句話說,只要下面四個條件有一個不具備,系統就不會出現死鎖。
〈1〉互斥條件。即某個資源在一段時間內只能由一個程序佔有,不能同時被兩個或兩個以上的程序佔有。這種獨佔資源如CD-ROM驅動器,印表機等等,必須在佔有該資源的程序主動釋放它之後,其它程序才能佔有該資源。這是由資源本身的屬性所決定的。如獨木橋就是一種獨佔資源,兩方的人不能同時過橋。
〈2〉不可搶佔條件。程序所獲得的資源在未使用完畢之前,資源申請者不能強行地從資源佔有者手中奪取資源,而只能由該資源的佔有者程序自行釋放。如過獨木橋的人不能強迫對方後退,也不能非法地將對方推下橋,必須是橋上的人自己過橋後空出橋面(即主動釋放佔有資源),對方的人才能過橋。
〈3〉佔有且申請條件。程序至少已經佔有一個資源,但又申請新的資源;由於該資源已被另外程序佔有,此時該程序阻塞;但是,它在等待新資源之時,仍繼續佔用已佔有的資源。還以過獨木橋為例,甲乙兩人在橋上相遇。甲走過一段橋面(即佔有了一些資源),還需要走其餘的橋面(申請新的資源),但那部分橋面被乙佔有(乙走過一段橋面)。甲過不去,前進不能,又不後退;乙也處於同樣的狀況。
〈4〉迴圈等待條件。存在一個程序等待序列{P1,P2,...,Pn},其中P1等待P2所佔有的某一資源,P2等待P3所佔有的某一源,......,而Pn等待P1所佔有的的某一資源,形成一個程序迴圈等待環。就像前面的過獨木橋問題,甲等待乙佔有的橋面,而乙又等待甲佔有的橋面,從而彼此迴圈等待。
上面我們提到的這四個條件在死鎖時會同時發生。也就是說,只要有一個必要條件不滿足,則死鎖就可以排除。
專案中死鎖的形成與解決
根據以上理論,以及斷點除錯,可以分析得專案的形成,如下圖所描述。
因此只要讓同步下載時不會掛起主執行緒就可以避免執行緒死鎖的情況發生。因此專案中使用NSURLConnet的同步請求方法實現下載即可, 因為它不會掛起主執行緒,還能讓其他執行緒往主執行緒中新增程式碼塊block。
NSData *gifImageData = [NSDatadataWithContentsOfURL:model.imageRemoteURL]; FLAnimatedImage *FLAImage = [FLAnimatedImageanimatedImageWithGIFData:gifImageData]; |
Important Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated. |
因此只要讓同步下載時不會掛起主執行緒就可以避免執行緒死鎖的情況發生。因此專案中使用NSURLConnet的同步請求方法實現下載即可, 因為它不會掛起主執行緒,還能讓其他執行緒往主執行緒中新增程式碼塊block。