1. 程式人生 > >多執行緒效能及效率問題

多執行緒效能及效率問題

一、[多執行緒概要]
  隨著計算機技術的發展,程式設計模型也越來越複雜多樣化。但多執行緒程式設計模型是目前計算機系統架構的最終模型。隨著CPU主頻的不斷攀升,X86架構的硬體已經成為瓶,在這種架構的CPU主頻最高為4G。事實上目前3.6G主頻的CPU已經接近了頂峰。
  如果不能從根本上更新當前CPU的架構(在很長一段時間內還不太可能),那麼繼續提高CPU效能的方法就是超執行緒CPU模式。那麼,作業系統、應用程式要發揮CPU的最大效能,就是要改變到以多執行緒程式設計模型為主的並行處理系統和併發式應用程式

  所以,掌握多執行緒程式設計模型,不僅是目前提高應用效能的手段,更是下一代程式設計模型的核心思想。多執行緒程式設計的目的,就是"最大限度地利用CPU資源",當某一執行緒的處理不需要佔用CPU而只和I/O,OEMBIOS等資源打交道時,讓需要佔用CPU資源的其它執行緒有機會獲得CPU資源。從根本上說,這就是多執行緒程式設計的最終目的。

二、[多執行緒概念理解]

     通常多執行緒的應用不是為了提高執行效率,而是為了提高資源使用效率。比如你的應用程式需要訪問網路,因為網路有延時,如果在介面執行緒訪問,那麼在網路訪問期間介面將無法響應使用者訊息,這是就應該使用多執行緒。  
 一、從程式執行角度理解  
     如果是單CPU,而且程式使用的資源僅僅是在記憶體和CPU,從執行指令上比較肯定單執行緒要比多執行緒精簡;時間的話,理想狀況可以認為單執行緒要比多執行緒時間短,但是實際情況很難講,因為Windows下是多工的,每一時刻CPU的使用情況都有不同;程式程式碼經過編譯器編譯也可能是優化過的;CPU對Cache的命中率也是隨機的。所以精確的比較其實很難實現。

    多執行緒程式設計的目的,就是"最大限度地利用CPU資源",當某一執行緒的處理不需要佔用CPU而只和I/O,OEMBIOS等資源打交道時,讓需要佔用CPU資源的其它執行緒有機會獲得CPU資源。每個程式執行時都會產生一個程序,而每一個程序至少要有一個主執行緒。這個執行緒其實是程序執行的一條線索,除了主執行緒外你還可以給程序增加其它的執行緒,也即增加其它的執行線索,由此在某種程度上可以看成是給一個應用程式增加了多工功能

    當程式執行後,您可以根據各種條件掛起或執行這些執行緒,尤其在多CPU的環境中,這些執行緒是併發執行的。多執行緒就是在一個程序內有多個執行緒。從而使一個應用程式有了多工的功能。多程序技術也可以實現這一點,但是建立程序的高消耗(每個程序都有獨立的資料和程式碼空間),程序之間通訊的不方便(訊息機制),程序切換的時間太長,這些導致了多執行緒的提出,對於單CPU來說(沒有開啟超執行緒),在同一時間只能執行一個執行緒,所以如果想實現多工,那麼就只能每個程序或執行緒獲得一個時間片,在某個時間片內,只能一個執行緒執行,然後按照某種策略換其他執行緒執行。由於時間片很短,這樣給使用者的感覺是同時有好多執行緒在執行。

      但是執行緒切換是有代價的,因此如果採用多程序,那麼就需要將執行緒所隸屬的該程序所需要的記憶體進行切換,這時間代價是很多的。而執行緒切換代價就很少,執行緒是可以共享記憶體的。所以採用多執行緒在切換上花費的比多程序少得多。但是,執行緒切換還是需要時間消耗的,所以採用一個擁有兩個執行緒的程序執行所需要的時間比一個執行緒的程序執行兩次所需要的時間要多一些。

     即採用多執行緒不會提高程式的執行速度,反而會降低速度,但是對於使用者來說,可以減少使用者的響應時間。上述結果只是針對單CPU,如果對於多CPU或者CPU採用超執行緒技術的話,採用多執行緒技術還是會提高程式的執行速度的。因為單執行緒只會對映到一個CPU上,而多執行緒會對映到多個CPU上,超執行緒技術本質是多執行緒硬體化,所以也會加快程式的執行速度。

二、從計算機程式設計角度理解

    在計算機程式設計中,一個基本的概念就是同時對多個任務加以控制。許多程式設計問題都要求程式能夠停下手頭的工作,改為處理其他一些問題,再返回主程序。可以通過多種途徑達到這個目的。最開始的時候,那些擁有機器低階知識的程式設計師編寫一些“中斷服務例程”,主程序的暫停是通過硬體級的中斷實現的。儘管這是一種有用的方法,但編出的程式很難移植,由此造成了另一類的代價高昂問題。

      有些時候,中斷對那些實時性很強的任務來說是很有必要的。但還存在其他許多問題,它們只要求將問題劃分進入獨立執行的程式片斷中,使整個程式能更迅速地響應使用者的請求。在一個程式中,這些獨立執行的片斷叫作“執行緒”(Thread),利用它程式設計的概念就叫作“多執行緒處理”。多執行緒處理一個常見的例子就是使用者介面。利用執行緒,使用者可按下一個按鈕,然後程式會立即作出響應,而不是讓使用者等待程式完成了當前任務以後才開始響應。


  最開始,執行緒只是用於分配單個處理器的處理時間的一種工具。但假如作業系統本身支援多個處理器,那麼每個執行緒都可分配給一個不同的處理器,真正進入“並行運算”狀態。從程式設計語言的角度看,多執行緒操作最有價值的特性之一就是程式設計師不必關心到底使用了多少個處理器。程式在邏輯意義上被分割為數個執行緒;假如機器本身安裝了多個處理器,那麼程式會執行得更快,毋需作出任何特殊的調校。

  根據前面的論述,大家可能感覺執行緒處理非常簡單。但必須注意一個問題:共享資源!如果有多個執行緒同時執行,而且它們試圖訪問相同的資源,就會遇到一個問題。舉個例子來說,兩個程序不能將資訊同時傳送給一臺印表機。為解決這個問題,對那些可共享的資源來說(比如印表機),它們在使用期間必須進入鎖定狀態。所以一個執行緒可將資源鎖定,在完成了它的任務後,再解開(釋放)這個鎖,使其他執行緒可以接著使用同樣的資源。多執行緒是為了同步完成多項任務,不是為了提高執行效率,而是為了提高資源使用效率來提高系統的效率。執行緒是在同一時間需要完成多項任務的時候實現的。

    執行緒(thread),有時被稱為輕量級程序(Lightweight Process,LWP),是程式執行流的最小單元。一個標準的執行緒由執行緒ID,當前指令指標(PC),暫存器集合和堆疊組成。
    另外,執行緒是程序中的一個實體,是被系統獨立排程和分派的基本單位,執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源,但它可與同屬一個程序的其它執行緒共享程序所擁有的全部資源。
    一個執行緒可以建立和撤消另一個執行緒,同一程序中的多個執行緒之間可以併發執行。由於執行緒之間的相互制約,致使執行緒在執行中呈現出間斷性。執行緒也有就緒、阻塞和執行三種基本狀態。
  執行緒是程式中一個單一的順序控制流程.在單個程式中同時執行多個執行緒完成不同的工作,稱為多執行緒.  

1、執行緒與程序
  (1)執行緒和程序的區別在於,子程序和父程序有不同的程式碼和資料空間,而多個執行緒則共享資料空間,每個執行緒有自己的執行堆疊和程式計數器為其執行上下文.多執行緒主要是為了節約CPU時間,發揮利用,根據具體情況而定. 執行緒的執行中需要使用計算機的記憶體資源和CPU
  通常在一個程序中可以包含若干個執行緒,它們可以利用程序所擁有的資源。在引入執行緒的作業系統中,通常都是把程序作為分配資源的基本單位,而把執行緒作為獨立執行和獨立排程的基本單位。
      由於執行緒比程序更小,基本上不擁有系統資源,故對它的排程所付出的開銷就會小得多,能更高效的提高系統內多個程式間併發執行的程度
  每個正在系統上執行的程式都是一個程序。每個程序包含一到多個執行緒。程序也可能是整個程式或者是部分程式的LX執行。執行緒是一組指令的集合,或者是程式的特殊段,它可以在程式裡獨立執行。也可以把它理解為程式碼執行的上下文。所以執行緒基本上是輕量級的程序,它負責在單個程式裡執行多任務。通常由作業系統負責多個執行緒的排程和執行。
  (2)程序和執行緒都是作業系統的概念。程序是應用程式的執行例項,每個程序是由私有的虛擬地址空間、程式碼、資料和其它各種系統資源組成,程序在執行過程中建立的資源隨著程序的終止而被銷燬,所使用的系統資源在程序終止時被釋放或關閉。
  執行緒是程序內部的一個執行單元。系統建立好程序後,實際上就啟動執行了該程序的主執行執行緒,主執行執行緒以函式地址形式,比如說main或WinMain函式,將程式的啟動點提供給Windows系統。主執行執行緒終止了,程序也就隨之終止。

  (3)每一個程序至少有一個主執行執行緒,它無需由使用者去主動建立,是由系統自動建立的。使用者根據需要在應用程式中建立其它執行緒,多個執行緒併發地運行於同一個程序中。一個程序中的所有執行緒都在該程序的虛擬地址空間中,共同使用這些虛擬地址空間、全域性變數和系統資源,所以執行緒間的通訊非常方便,多執行緒技術的應用也較為廣泛。多執行緒可以實現並行處理,避免了某項任務長時間佔用CPU時間。要說明的一點是,目前大多數的計算機都是單處理器(CPU)的,為了執行所有這些執行緒,作業系統為每個獨立執行緒安排一些CPU時間,作業系統以輪換方式向執行緒提供時間片,這就給人一種假象,好象這些執行緒都在同時執行。由此可見,如果兩個非常活躍的執行緒為了搶奪對CPU的控制權,線上程切換時會消耗很多的CPU資源,反而會降低系統的效能。這一點在多執行緒程式設計時應該注意。Win32 SDK函式支援進行多執行緒的程式設計,並提供了作業系統原理中的各種同步、互斥和臨界區等操作。Visual C++ 6.0中,使用MFC類庫也實現了多執行緒


2、使用執行緒的好處有以下幾點:
  ·使用執行緒可以把佔據長時間的程式中的任務放到後臺去處理
  ·使用者介面可以更加吸引人,這樣比如使用者點選了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度
  ·程式的執行速度可能加快
  ·在一些等待的任務實現上如使用者輸入、檔案讀寫和網路收發資料等,執行緒就比較有用了。在這種情況下可以釋放一些珍貴的資源如記憶體佔用等等。
  還有其他很多使用多執行緒的好處,這裡就不一一說明了。一些執行緒模型的背景

3、在Win32環境中常用的一些模型
  ·單執行緒模型
  在這種執行緒模型中,一個程序中只能有一個執行緒,剩下的程序必須等待當前的執行緒執行完。這種模型的缺點在於系統完成一個很小的任務都必須佔用很長的時間。
  ·塊執行緒模型(單執行緒多塊模型STA)
  這種模型裡,一個程式裡可能會包含多個執行的執行緒。在這裡,每個執行緒被分為程序裡一個單獨的塊。每個程序可以含有多個塊,可以共享多個塊中的資料。程式規定了每個塊中執行緒的執行時間。所有的請求通過Windows訊息佇列進行序列化,這樣保證了每個時刻只能訪問一個塊,因而只有一個單獨的程序可以在某一個時刻得到執行。這種模型比單執行緒模型的好處在於,可以響應同一時刻的多個使用者請求的任務而不只是單個使用者請求。但它的效能還不是很好,因為它使用了序列化的執行緒模型,任務是一個接一個得到執行的。
  ·多執行緒塊模型(自由執行緒塊模型)
  多執行緒塊模型(MTA)在每個程序裡只有一個塊而不是多個塊。這單個塊控制著多個執行緒而不是單個執行緒。這裡不需要訊息佇列,因為所有的執行緒都是相同的塊的一個部分,並且可以共享。這樣的程式比單執行緒模型和STA的執行速度都要塊,因為降低了系統的負載,因而可以優化來減少系統idle的時間。這些應用程式一般比較複雜,因為程式設計師必須提供執行緒同步以保證執行緒不會併發的請求相同的資源,因而導致競爭情況的發生。這裡有必要提供一個鎖機制。但是這樣也許會導致系統死鎖的發生。


四、[第一需要弄清的問題]
  如同程式和程序的區別,要掌握多執行緒程式設計,第一要弄清的問題是:執行緒物件和執行緒的區別。
  執行緒物件是可以產生執行緒的物件。比如在java平臺中Thread物件,Runnable物件。執行緒,是指正在執行的一個指點令序列。在java平臺上是指從一個執行緒物件的start()開始,執行run方法體中的那一段相對獨立的過程。
  天下難事必始於易,天下大事必始於細。
  讓我們先從最簡單的"單執行緒"來入手:(1)帶引號說明只是相對而言的單執行緒,(2)基於java

class BeginClass{

        public static void main(String[] args){

           for(int i=0;i<100;i++)

            System.out.println("hello ,world");

     }

}
  如果我們成功編譯了該java檔案,然後在命令列上敲入:java BeginClass
  現在發生了什麼呢?  JVM程序被啟動,在同一個JVM程序中,有且只有一個程序,就是它自己。然後在這個JVM環境中,所有程式的執行都是以執行緒來執行。JVM最先會產生一個主執行緒,由它來執行指定程式的入口點。在這個程式中,就是主執行緒從main方法開始執行。當main方法結束後,主執行緒執行完成。JVM程序也隨之退出。

  我們看到的是一個主執行緒在執行main方法,這樣的只有一個執行緒執行程式邏輯的流程我們稱之為單執行緒。這是JVM提供給我們的單執行緒環境,事實上,JVM底層還至少有垃圾回收這樣的後臺執行緒以及其它非java執行緒,但這些執行緒對我們而言不可訪問,我們只認為它是單執行緒的。

  主執行緒是JVM自己啟動的,在這裡它不是從執行緒物件產生的。在這個執行緒中,它運行了main方法這個指令序列。理解它,但它沒有更多可以研究的內容。

  [接觸多執行緒]

class MyThread extends Thread{
 public void run(){
  System.out.println("Thread say:Hello,World!");
 }
}

public class MoreThreads{
 public static void main(String[] args){
  new MyThread();
  new MyThread().start();
  System.out.println("Main say:Hello,World");
 }
}


  執行這個程式,main方法第一行產生了一個執行緒物件,但並沒有執行緒啟動。
  main方法第二行產生了一個執行緒物件,並啟動了一個執行緒。
  main方法第三行,產生並啟動一個執行緒後,主執行緒自己也繼續執行其它語句

  我們先不研究Thread物件的具體內容,稍微來回想一下上面的兩個概念,執行緒物件和執行緒。在JAVA中,執行緒物件是JVM產生的一個普通的Object子類。而執行緒是CPU分配給這個物件的一個執行過程。我們說的這個執行緒在幹什麼,不是說一個執行緒物件在幹什麼,而是這個執行過程在幹什麼。