《Linux多線程服務端編程》筆記——線程同步精要


並發編程基本模型
message passing和shared memory。

線程同步的四項原則

  • 盡量最低限度地共享對象,減少需要同步的場合。如果確實需要,優先考慮共享 immutable 對象。
  • 使用高級的並發編程構件,如TaskQueue、Producer-Consumer Queue、CountDownLatch等等。
  • 不得已必須使用底層同步原語(primitives)時,只用非遞歸的互斥器和條件變量,慎用讀寫鎖,不要用信號量。
  • 除了使用 atomic 整數之外,不自己編寫 lock-free 代碼,也不要用“內核級”同步原語。不憑空猜測“哪種做法性能會更好”,比如 spin lock vs. mutex。

互斥器的使用

  • 用 RAII 手法封裝 mutex 的創建、銷毀、加鎖、解鎖這四個操作。保證鎖的生效期間等於一個作用域(scope)。
  • 只用非遞歸的 mutex(即不可重入的 mutex)。
  • 不手工調用 lock() 和 unlock() 函數,一切交給棧上的 Guard 對象的構造和析構函數負責(Scoped Locking)。
  • 在每次構造 Guard 對象的時候,思考一路上(調用棧上)已經持有的鎖,防止因加鎖順序不同而導致死鎖。

條件變量的使用

  • 對於 wait() 端:
    必須與 mutex 一起使用,該布爾表達式的讀寫需受此 mutex 的保護。
    在 mutex 已上鎖的情況下才能調用 wait()。
    把判斷布爾表達式和 wait() 放在 while 循環中。

  • 對於 signal/broadcast 端:
    不一定要在 mutex 已上鎖的情況下調用 signal(理論上)。
    在 signal 之前一般要修改布爾表達式。
    修改布爾表達式通常需要用 mutex 保護(至少用作 full memory barrier)。
    broadcast 通常用於表明狀態變化,signal 通常用於表示資源可用。

  • 虛假喚醒(spurious wakeup),Linux 中 futex 慢速系統調用被信號打斷返回 -1,wait 返回了。

讀寫鎖與信號量的使用

  • 從正確性方面來說,一種典型的易犯的錯誤是在持有 reader lock 的時候修改了共享數據。
  • 從性能方面來說,讀寫鎖不見得比普通 mutex 更高效。
  • reader lock 可能允許提升為 writer lock,也可能不允許提升。
  • 通常 reader lock 是可重入的,writer lock 是不可重入的。
  • 信號量不是必備的同步原語,因為條件變量配合互斥器可以完全替代其功能,而且更不易用錯。

參考:《Linux多線程服務端編程》。


免責聲明!

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



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