不再安全的 OSSpinLock

昨天有位開發(fā)者在Github上給我提了一個issue,里面指出?OSSpinLock?在新版?iOS?中已經(jīng)不能再保證安全了,并提供了幾個相關(guān)資料的鏈接。我仔細(xì)查了一下相關(guān)資料,確認(rèn)了這個讓人不爽的?bug。

OSSpinLock?的問題

2015-12-14?那天,swift-dev?郵件列表里有人在討論?weak?屬性的線程安全問題,其中有幾位蘋果工程師透露了自旋鎖的 bug,對話內(nèi)容大致如下:

新版?iOS?中,系統(tǒng)維護(hù)了?5?個不同的線程優(yōu)先級/QoS: background,utility,default,user-initiated,user-interactive。高優(yōu)先級線程始終會在低優(yōu)先級線程前執(zhí)行,一個線程不會受到比它更低優(yōu)先級線程的干擾。這種線程調(diào)度算法會產(chǎn)生潛在的優(yōu)先級反轉(zhuǎn)問題,從而破壞了?spin lock。

具體來說,如果一個低優(yōu)先級的線程獲得鎖并訪問共享資源,這時一個高優(yōu)先級的線程也嘗試獲得這個鎖,它會處于?spin lock?的忙等狀態(tài)從而占用大量?CPU。此時低優(yōu)先級線程無法與高優(yōu)先級線程爭奪?CPU?時間,從而導(dǎo)致任務(wù)遲遲完不成、無法釋放?lock。這并不只是理論上的問題,libobjc?已經(jīng)遇到了很多次這個問題了,于是蘋果的工程師停用了?OSSpinLock。

蘋果工程師?Greg Parker?提到,對于這個問題,一種解決方案是用?truly unbounded backoff?算法,這能避免?livelock?問題,但如果系統(tǒng)負(fù)載高時,它仍有可能將高優(yōu)先級的線程阻塞數(shù)十秒之久;另一種方案是使用?handoff lock?算法,這也是?libobjc?目前正在使用的。鎖的持有者會把線程?ID?保存到鎖內(nèi)部,鎖的等待者會臨時貢獻(xiàn)出它的優(yōu)先級來避免優(yōu)先級反轉(zhuǎn)的問題。理論上這種模式會在比較復(fù)雜的多鎖條件下產(chǎn)生問題,但實踐上目前還一切都好。

libobjc?里用的是?Mach?內(nèi)核的?thread_switch()?然后傳遞了一個?mach thread port?來避免優(yōu)先級反轉(zhuǎn),另外它還用了一個私有的參數(shù)選項,所以開發(fā)者無法自己實現(xiàn)這個鎖。另一方面,由于二進(jìn)制兼容問題,OSSpinLock?也不能有改動。

最終的結(jié)論就是,除非開發(fā)者能保證訪問鎖的線程全部都處于同一優(yōu)先級,否則?iOS?系統(tǒng)中所有類型的自旋鎖都不能再使用了。

OSSpinLock 的替代方案

為了找到一個替代方案,我做了一個簡單的性能測試,對比了一下幾種能夠替代?OSSpinLock?鎖的性能。測試是在?iPhone6、iOS9?上跑的,代碼在這里。這里只是測試了單線程的情況,不能反映多線程下的實際性能,所以這個結(jié)果只能當(dāng)作一個定性分析

可以看到除了?OSSpinLock?外,dispatch_semaphore?和?pthread_mutex?性能是最高的。有消息稱,蘋果在新系統(tǒng)中已經(jīng)優(yōu)化了?pthread_mutex?的性能,所以它看上去和?OSSpinLock?差距并沒有那么大了。

社區(qū)反應(yīng)

蘋果

查看 CoreFoundation 的源碼能夠發(fā)現(xiàn),蘋果至少在?2014?年就發(fā)現(xiàn)了這個問題,并把CoreFoundation?中的?spinlock?替換成了?pthread_mutex,具體變化可以查看這兩個文件:CFInternal.h(855.17)CFInternal.h(1151.16)。蘋果自己發(fā)現(xiàn)問題后,并沒有及時更新OSSpinLock?的文檔,也沒有告知開發(fā)者,這有些讓人失望。

在 iOS 10/macOS 10.12 發(fā)布時,蘋果提供了新的?os_unfair_lock 作為 OSSpinLock 的替代,并且將 OSSpinLock 標(biāo)記為了 Deprecated。

Google

google/protobuf?內(nèi)部的?spinlock 被全部替換為?dispatch_semaphore,詳情可以看這個提交:https://github.com/google/protobuf/pull/1060。用?dispatch_semaphore 而不用?pthread_mutex 應(yīng)該是出于性能考慮。

相關(guān)鏈接

https://blog.csdn.net/fishmai/article/details/119793819

https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000344.html

http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/

http://engineering.postmates.com/Spinlocks-Considered-Harmful-On-iOS/

https://twitter.com/steipete/status/676851647042203648

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容