李劭卓
(河北農業大學渤海校區,黃驊 061000)
在計算機發展史上,計算機從單線程發展到了多線程,多線程的出現很大程度上提升了計算機的性能,但是在人類科技發展的需求以及摩爾定理的限制下,單核處理機系統已經到達極限。為了響應更多計算機性能方面的需求,適應計算機發展的需要,計算機已開始從單處理器系統向多處理器系統發展。
在多核處理機系統的快速發展中,多核系統編程(任務識別、平衡、數據分割、數據依賴、測試與調試)和程序正確性(同步與死鎖)的兩個方面難題越發突出。為了解決這些難題并設計出更好的多線程程序,人們開始通過編譯器與運行時庫來創建和管理多線程,即隱式線程(implicit threading),基于隱式線程與多線程的主要設計方法有三種:線程池(Thread pool)、OpenMP、大中央調度(Grand Central Dispatch,GCD)。
當多線程服務器接收到一個請求時,如果采用創建一個新的線程去處理請求會出現兩種問題。第一種:創建新的線程需要時間并且在處理后會被丟失,第二種:沒有限制系統內部并發執行線程的數量。這兩個問題會導致系統響應速度以及性能的降低。
而線程池的出現為線程的創建、銷毀以及資源不足的問題提供了解決方案。在進程開始的時候,系統就創建一定數量的線程,并將這些線程加入池中等待喚醒,當服務器收到請求時,服務器就喚醒一個可用的線程,然后將需要服務的請求傳遞給該線程,待線程完成服務之后,再重新返回到線程池中等待下一次喚醒,若沒有可用的線程,則服務器就會開始等待,直到有空線程為止。
優點:
(1)提高了系統的響應速度。系統已經創建好了一定數量的線程,當有請求到來時,可直接使用預先建立好的線程,不需要重新創建線程,這消除了系統創建線程時的時間延遲。
(2)線程池限制了任何時刻的可用線程的數量。系統預先創建一定數量的線程,并且限制它們的數量,這能夠控制線程對象在內存中的消耗。十分有利于有大量并發式訪問,而其任務短小的服務器。
(3)將待執行任務從創建任務的機制中分離出來。允許使用延遲或定期執行的策略運行任務。
(4)降低系統的開銷與資源的消耗。線程池中的線程被重復利用,使用后的線程會重新返回到線程池中,不會被銷毀,降低了虛擬機垃圾回收方面的開銷。
缺點:
(1)需要考慮線程池的大小。線程池中的線程并非越多越好,需要結合軟硬件的具體環境以及應用程序自身的特點來考慮。
(2)線程泄露。當線程執行完任務后,沒有返回到線程池中。需要采取異常捕捉的方法防止線程泄露。
(3)并發錯誤。要從邏輯上考慮線程的并發狀況,防止死鎖的出現。
OpenMP是一種支持共享內存環境的多處理器多線程并行編譯語言,它由一組編譯指令和API組成,其通過向C、C++、Fortran語言提供一組和平臺無關的編譯指令、函數調用、指導函數以及環境變量,來指示OpenMP運行時庫在何時何處才能進行并行處理。
并行處理的指示指令為#pragma omp parallel,當程序進行到指示指令時,系統就會創建一個與系統處理器數量一樣多的線程。原線程(主線程)與新派生出來的線程(組的從屬線程)組成線程組,即從單個線程執行轉換為多線程并行執行,待程序執行完并行區域(parallel fegion)的所有內容后,系統將會把并行線程的所得結果連接在一起,并重新開始單個線程執行,此通常用于循環結構的并行化處理。
除提供并行化指令外,OpenMP還允許手動設置線程數量、指定哪個數據可在線程間共享、哪個數據只屬于某個線程,其支持的操作系統有Windows、Linux、Mac OS X,并可用于多種開源編譯器和商用編譯器。
優點:
(1)有利于多核升級后的程序性能。多核編程需要考慮到今后硬件核數升級之后的程序性能問題,如果將程序設計為隨CPU核數增長,就無法固定線程的數量;如果固定線程數量并且要求CPU核數增加時,程序性能也要增加,則需要在每次硬件核數升級后,程序進行相對應的更改。OpenMP可直接根據CPU的核數創建線程,這十分方便。
(2)并行區域執行方便。多核編程中,需要將并行部分均分到各個CPU核心中,這對計算的負載均衡要求很高,但OpenMP可直接將并行區域的代碼分解為多個線程執行。
(3)方便移植。OpenMP是標準規范,所有支持OpenMP的編譯器執行標準一致。
缺點:
(1)使用范圍有限。OpenMP是一種支持共享內存環境的多處理器多線程并行編譯語言,在非共享內存系統中無法使用。
(2)高層抽象的局限性。OpenMP在需要復雜線程同步與互斥場合中不適用。
大中央調度是Apple Mac OS X和iOS的一種技術,它能為C語言、API和運行時庫提供一組擴展,允許多個代碼區域并行運行。
GCD在C與C++中增加了塊(block)的擴展,用字符加{ }的形式將塊括起來,同時以一個塊為獨立單位,然后將各個塊放入調度隊列(dispatchqueue)中,在執行程序時
,每次從調度隊列中取出一個塊,并將它分配給線程池中的一個可用線程。
調度隊列有兩種形式:串行(serial)和并行(concurrent)。
串行:
串行隊列以先進先出的形式進行塊的執行,每次只能執行一個塊,每當一個塊執行完后,才會從調度隊列中調度下一個塊。在進程中可以有多個串行隊列,但是只能有一個主隊列,其用于保證任務的順序的正確性。
并行:
并發隊列也以先進先出的進行塊的執行,但是每次可以執行多個塊,進行多個塊的并行運行。
在大中央調度中的線程池由POSIX線程組成,GCD會根據應用需求以及系統的容量動態地調節線程的數量,以此實現對內部線程池的管理。
優點:
(1)GCD更加接近底層。GCD提供底層函數,在追求性能的底層操作中,速度很快,并提供更強大的控制力。
(2)系統資源利用率高。可以簡單地在不同代碼中傳遞上下文,減少上下文之間的切換,提高資源的利用率,減輕系統的負荷。
缺點:
操作實現復雜。GCD需要許多代碼來實現異步操作之間的事務性、順序行和依賴關系。
線程構造模塊(Treading Building Block,TBB):
一種由Intel開發用于并行編程的C++線程庫,它不需要線程編程,允許運行時庫自動地將邏輯并行映射到線程中,有效地利用了CPU資源,而且可以與其他線程兼容,實現無縫共存。
Java.util.concurrent:
一種Java語言用于并行編程的實用工具類,它包括了幾個已經標準化的可擴展框架和一些提供有用功能的類。
多核多線程已成為處理器發展的一個大方向,它能提升計算機的性能,但面臨著多核系統編程和程序正確性上的兩個方面難題,隱式多線程可以有效地解決與緩解這兩方面問題。
雖然隱式多線程實現技術很多,但其核心思想是控制系統中線程的數量,利用已有線程執行任務、創建線程數量要與計算機處理核心相匹配以及動態地調用線程數量。