《Linux多線程服務端編程》筆記——多線程服務器的適用場合


如果要在一台多核機器上提供一種服務或執行一個任務,可用的模式有

  • 運行一個單線程的進程
  • 運行一個多線程的進程
  • 運行多個單線程的進程
  • 運行多個多線程的進程

這些模式之間的比較已經是老生常談,簡單地總結

  • 模式 1 是不可伸縮的 (scalable),不能發揮多核機器的計算能力;
  • 模式 3 是目前公認的主流模式。它有兩種子模式:
    3a 簡單地把模式 1 中的進程運行多份,如果能用多個 tcp port 對外提供服務的話;
    3b 主進程+woker進程,如果必須綁定到一個 tcp port,比如 httpd+fastcgi。
  • 模式 2 是很多人鄙視的,認為多線程程序難寫,而且不比模式 3 有什么優勢;
  • 模式 4 更是千夫所指,它不但沒有結合 2 和 3 的優點,反而匯聚了二者的缺點。

據我所知,有兩種場合必須使用單線程

  • 程序可能會 fork()。
  • 限制程序的 CPU 占用率。

單線程程序的優缺點

  • event loop 有一個明顯的缺點,它是非搶占的(non-preemptive)。假設事件 a 的優先級高於事件 b,處理事件 a 需要 1ms,處理事件 b 需要 10ms。如果事件 b 稍早於 a 發生,那么當事件 a 到來時,程序已經離開了 poll() 調用開始處理事件 b。事件 a 要等上 10ms 才有機會被處理,總的響應時間為 11ms。這等於發生了優先級反轉。這可缺點可以用多線程來克服,這也是多線程的主要優勢。
  • 如果用很少的 CPU 負載就能讓的 IO 跑滿,或者用很少的 IO 流量就能讓 CPU 跑滿,那么多線程沒啥用處。

多線程的適用場景

  • 提高響應速度,讓 IO 和“計算”相互重疊,降低 latency。雖然多線程不能提高絕對性能,但能提高平均響應性能。

模式 2 和模式 3a 該如何取舍
如果工作集較大,那么就用多線程,避免 CPU cache 換入換出,影響性能;否則,就用單線程多進程,享受單線程編程的便利。

一個程序要做成多線程的,大致要滿足

  • 有多個 CPU 可用。單核機器上多線程的優勢不明顯;
  • 線程間有共享數據。如果沒有共享數據,用模型 3b 就行。雖然我們應該把線程間的共享數據降到最低,但不代表沒有;
  • 共享的數據是可以修改的,而不是靜態的常量表。如果數據不能修改,那么可以在進程間用 shared memory,模式 3 就能勝任;
  • 提供非均質的服務。即,事件的響應有優先級差異,我們可以用專門的線程來處理優先級高的事件。防止優先級反轉;
  • latency 和 throughput 同樣重要,不是邏輯簡單的 IO bound 或 CPU bound 程序;
  • 利用異步操作。比如 logging。無論往磁盤寫 log file,還是往 log server 發送消息都不應該阻塞 critical path;
  • 能 scale up。一個好的多線程程序應該能享受增加 CPU 數目帶來的好處,目前主流是 8 核,很快就會用到 16 核的機器了;
  • 具有可預測的性能。隨着負載增加,性能緩慢下降,超過某個臨界點之后急速下降。線程數目一般不隨負載變化;
  • 多線程能有效地划分責任與功能,讓每個線程的邏輯比較簡單,任務單一,便於編碼。而不是把所有邏輯都塞到一個 event loop 里,就像 Win32 SDK 程序那樣。

一個多線程服務程序中的線程大致可分為 3 類

  • IO 線程,這類線程的的主循環是 io multiplexing,等在 select/poll/epoll 系統調用上。這類線程也處理定時事件。當然它的功能不止 IO,有些計算也可以放入其中;
  • 計算線程,這類線程的主循環是 blocking queue,等在 condition variable 上。這類線程一般位於 thread pool 中;
  • 第三方庫所用的線程,比如 logging,又比如 database connection。

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


免責聲明!

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



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