歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> 關於Unix >> 服務器診所:並不是僅僅就是線程化而已

服務器診所:並不是僅僅就是線程化而已

日期:2017/3/6 15:49:55   编辑:關於Unix
人們普遍對並發 — 多處理 — 存有誤解。本月的“ 服務器 診所”要介紹基本的並發概念,為了使您的業務在服務器機櫃中 安全 地進行,您需要這些並發概念。 人們對多處理這個名稱存在許多誤解。多數理論課程和許多編程教科書都清楚地解釋了並發概念,但並發是
  人們普遍對並發 — 多處理 — 存有誤解。本月的“服務器診所”要介紹基本的並發概念,為了使您的業務在服務器機櫃中安全地進行,您需要這些並發概念。
  
  人們對多處理這個名稱存在許多誤解。多數理論課程和許多編程教科書都清楚地解釋了並發概念,但並發是一個很難的主題,幾乎我們所有人都需要進修。
  
  並發指的是一次有不止一個“應用程序”在運行的情形。這裡我引用“應用程序”是因為它的意義是依賴於上下文的。Linux 主機總是把一組或多或少同時執行的程序(網絡協議守護程序、cron 管理程序、內核本身,常常還有更多)填到主機的進程表中。Linux 是一個多任務的操作系統。它就是為這樣的任務而構建的。
  
  在典型的單處理器主機上,任務實際上並不是同時執行的。內核中稱為調度程序的部分將工作換進換出,從而讓所有工作都獲得一輪執行。在同一個時間間隔內,您的浏覽器在下載東西,同時您在編輯程序源代碼,同時還在播放音樂。並發常常與這種同時出現聯系在一起。
  
  並發的兩個方面
  請記住,從“用戶視圖”或“編程模型”的角度來看,並發就是關於調度對不可分資源的訪問這樣一件事情。然而,與之相對的是"後端"意義上的並發。尋求原始性能的人們把重點放在這個不同的方面。在他們看來,“多處理”的意思通常是把單個任務分為幾個部分,使不同的中央處理器(central processing unit,CPU)能協同完成任務。其思想是按照外部時鐘的步調在更短的時間內完成一項工作,即便要以更復雜的硬件和編程為代價也在所不惜。
  
  並發的這兩個方面都與調度,即將任務分配給 CPU 有關。兩者都影響可用性。然而將這兩個方面混淆起來卻是人們常犯而又棘手的錯誤。初學編程的人似乎特別容易迷信最重要的並發方法之一 — “多線程”。“多線程”常簡稱為“線程化”,對它的錯誤印象包括以下一些想法:
  
  線程化使程序運行得更快。
  線程化是唯一的並發構造,或者是唯一切實可行的並發構造。
  N 路主機的速度大約是單處理主機的 N 倍。
  只要對概念作一點點澄清就可以很快地糾正這些錯誤想法。
  
  天真的開發者常常這樣問道,“我的程序太慢了;我可以怎樣通過線程化來加快程序的運行?”答案通常是,“辦不到。”對現有的單任務應用程序作簡單的轉換以將它分為幾個多任務的部分總是會導致需要更多的計算。一般來說,將這樣一個程序“線程化”會使程序要用去更長時間。
  
  這種錯誤的說法會長久存在,當然有其理由。許多程序都可以分解為幾個部分,從而減輕瓶頸問題。將計算密集的工作 — 比如,對航天飛機重返大氣層的模擬 — 分散給八個 CPU 而不是一個 CPU 去做,其完成速度將快得多。更常見的是重構一個程序,以避免輸入/輸出(input/output,I/O)“阻塞”。如果您的消費者級應用程序能在等待鍵盤輸入、等待從磁盤調入的數據或等待通過網絡傳來的消息的時候做一些有用的工作,那看起來就像無代價地“提高了速度”似的。
  
  線程化的局限性
  但是,相信線程化會帶來這些加速是要冒風險的。這些加速都依賴於更深層的分析;僅當存在未充分利用的資源可用的情況下,才有可能提高速度。此外,線程化並不是實現這些並發的唯一辦法,並且常常也不是最好的辦法。
  
  學術文獻研究了至少十二種很重要的投入到了實際應用的並發模型。除聽說過線程化外,您多半也聽說過多處理(從程序的意義上說)、協同例程和基於事件的編程,也可能聽說過連續(continuation)、生成器和幾個更神秘難懂的構造。比如,如果您有一種支持生成器但不支持線程的語言,那您可以用生成器來模擬線程(反之亦然),則從這個意義上說,上述所有方法就都有一個形式上大致對等的東西。
  
  然而,編寫合適的程序不同於抽象的對等。在您按進度努力交付可靠的應用程序時,並發模型之間存在確實的不同。舉例來說,線程存在很多年來人們已經知道的脆弱性。線程是相當低級別的編程構造。要用線程安全地編寫程序是很難的;需要操縱線程的程序很容易引起不一致的數據、死鎖、不可伸縮的鎖定以及倒置的優先級等問題。Java 最初打算只支持多線程,將它作為並發的核心概念,最近,由於線程化的性能問題,Java 放棄了這一打算。能調試線程的調試器開銷巨大,這一點早已臭名昭著。
  
  不過,也不全是壞消息。如果您花點時間清楚地理解一些基本概念,那麼使用線程就象使用 XML 或 LDAP 或在其它任何專門領域那樣可靠。更直接地說,對於許多情形是存在更安全的 — 有時也是更快的!— 並發模型的。
  
  在許許多多情形中,使一個應用程序成為多任務的最好辦法是將它分解為多個相互合作的進程而不是線程。程序員通常都拒絕接受這一現實。其中一個原因是歷史的:進程過去常常比線程“重”得多,並且在多數版本的 Windows 中現在仍然如此。然而,在現代的 Linux 中,不同進程之間的上下文切換所花的時間只比同一進程的線程之間相應的上下文切換多 15%。時間上的花費所帶來的回報是理解得更深刻的並且更健壯的編程模型。許多程序員都可以編寫安全的獨立進程。但能編寫安全的線程的程序員卻相當少。
  
  什麼時候更適合采用多進程而不是多線程呢?舉例來說,假如您有一個“控制面板”圖形用戶界面(graphical user interface,GUI),它監視幾個大型計算的結果、檢索和更新數據庫記錄、甚至可能會報告外部物理設備的狀態。您可以將所有這些任務放到一個進程中,針對每項任務有一個獨立的線程。在 Windows 中,這常常是優先采用的做法。
  
  不過,就我的開發實踐來說,我通常是把每項任務放到它自己的進程中,通過套接字、管道或不時共享的內存進行彼此間的通信。由於您可以使用所有您常用的命令行工具來實現獨立進程的自動化,所以,上述做法極大簡化了單元測試。一個進程的崩潰不會危及其它任何進程。其性能通常與采用多線程不相上下,而且有時還更好,這取決於具體的硬件和編程狀況。
  
  其它並發模型
  這樣一種多進程實現常常依賴於基於事件的編程。事件是一個不同的並發概念,對管理 I/O 和相關的多任務職責很有用。事件將異步的“外部動作”與編程上的回調(也稱為信號、綁定等等)聯系起來。思考一下前面所說的 GUI 控制面板;在 Unix 中編寫這個程序時,一種高性能的做法是只在 select() 系統調用檢測到到達的數據時才更新顯示。使用 C 的程序員常常用 select 來標記基於事件的方法。
  
  您可能會把“協同例程”或“生成器”看作是課堂以外的內容。然而,它們已被內置在諸如 Modula 和 Icon 之類語言的定義中,因為它們在使多任務編程變得非常強大的同時又讓它仍然易於理解(從而也仍然是安全的)。如果您有復雜的性能要求,如果您的應用程序是以幾百個子任務的方式進行了最佳建模,尤其是如果您的服務器房間放置著大量的多路主機,那您應該研習更廣范圍的並發模型。您將發現每種模型都有它最適合的情形。其中某個並發模型可能會適合您自己的需要。
  
  同時,請意識到以下一點,即您或許要為您想在 Linux 中使用的任何模型尋求支持。下面的參考資料指向了一些用各種並發模型進行的實現和實驗,還有其它參考資料。
  
  多路難題
  最後要注意的一點:不要假設您的多任務軟件會在您的多處理(常常是“對稱多處理”(symmetric multiprocessing,SMP))硬件上很好地運行。特別是對於 Linux 的各種較老的版本,要從 SMP 機器中獲得有用的結果,常常需要用到很專業的知識。在用多個(最多 4 個,有時可以更多)處理器來處理不同的進程時,Linux 2.4 的缺省安裝可以做得很好。然而,一個進程中的多個線程可能會在單個處理器上造成瓶頸,而其它處理器卻處於空閒狀態。其它並發方法有時也會造成類似的問題。
  
  避免這些資源浪費有賴於您平台的具體狀況。有了 Linux 2.4 和流行的多路硬件,您可以合理地期望用缺省的“內核線程”(請參閱參考資料中的 Linux 線程常見問題解答)來恰當地調度線程,調度線程就是將這些線程分配給不同的 CPU。請使用 top 和其它系統管理工具來驗證調度是正確的,關於實際使用的線程調度方面的具體問題,請向您的 Linux 供應商或用戶組詢問。
  
  在您的多數編程工作中,您可能是將程序自然地分解為不同的邏輯任務。清楚地理解基本的並發概念,您就可以應用它們來滿足您自己的要求。請記住,並發既有面向前方的方面又有面向後方的方面:“用戶視圖”或“編程模型”控制您如何與應用程序交互的功能,而“後端”則管理把任務分配給硬件的工作。嚴格地區分您的功能和性能要求。最後,請記住,談到並發並不是僅僅就是線程化而已。通過使用明顯不是多線程的模型進行編程,您常常可以充分利用您的服務器。

Copyright © Linux教程網 All Rights Reserved