最近看到一篇關(guān)于信號量的文章:
AFNetWorking與dispatch_semaphore_t能共存嗎
答案是不能!
作者本來給出了解釋, 無奈原答案刪除了, 我試著解答一波
先說結(jié)論: 發(fā)生了死鎖!
- 原因: AFN框架已經(jīng)把
success
和failure
的回調(diào)放在了主線程中. 所以程序在走到dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOEVER)
這句代碼的時候, 已經(jīng)將主線程鎖住了, 所以同處于主線程的success
和failure
的回調(diào)block壓根就不會走, 所以同時放在主線程中的dispatch_semaphore_signal(semaphore)
同樣也不會走, 那么信號量的
value
值也不可能增大, 所以value
值一直為0
, 那么被鎖住的主線程就永遠鎖住了.
但是如果使用蘋果的NSURLSession
的話, 就不會死鎖!
- 原因: 蘋果原生的
completionHandler
回調(diào)并沒有放在主線程中, 所以當(dāng)主線程處于等待狀態(tài)中時,completionHandler
依然在異步執(zhí)行, 當(dāng)執(zhí)行完畢時,semaphore
的value
值+1
, 那么被鎖住的線程就被喚醒, 就會繼續(xù)執(zhí)行下面的代碼, 而不會發(fā)生死鎖!
其實整個下來就三四個方法:
dispatch_semaphore_t dispatch_semaphore_create(long value);
這個方法就是利用給定的value值創(chuàng)建一個計數(shù)信號
信號值必須是>=0
的整數(shù)
當(dāng)不再使用信號時, 必須調(diào)用dispatch_release
來釋放semaphore
對象
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
這個方法首先將semaphore
對象的信號值-1
, 當(dāng)semaphore
對象的信號值<0
時, 當(dāng)前線程鎖住(處于休眠狀態(tài)), 休眠的時間由timeout
參數(shù)決定.
DISPATCH_TIME_FOREVER
這個宏表示無限期休眠.
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
這個方法首先將semaphore
對象的信號值+1
, 當(dāng)semaphore
對象的信號值>=0
時, 當(dāng)前線程被喚醒, 得以繼續(xù)執(zhí)行隊列中的其他任務(wù).
那我們再回到圖2的代碼:
這段代碼中有2個線程: 主線程和session
回調(diào)方法所處的子線程.
當(dāng)子線程中拿不到回調(diào)數(shù)據(jù)的話, 主線程將會一直處于休眠狀態(tài).
所以當(dāng)這段代碼中dispatch_semaphore_wait
以下的代碼開始執(zhí)行時, 一定是拿到session
的回調(diào)數(shù)據(jù)了的.
信號量的方法通常用來管理線程池,
但是一定要注意semaphore
的value
值的平衡, 調(diào)用了dispatch_semaphore_signal
方法, 就需要再某處調(diào)用dispatch_semaphore_wait
方法, 這有點類似于管理OC
對象的引用計數(shù)
.
但是使用不好也會有諸多不便, 比如由于處理不當(dāng), 經(jīng)常出現(xiàn)主線程死鎖的情況. 這樣就得不償失了.