多線程編程--線程基礎(一)

      都說操做系統是用戶體驗驅動其發展的,在好久好久的Micrisoft16Windows操做系統中,那是單線程並且是不能搶佔的CPU的操做系統,這樣致使了當某個線程發生死鎖或者不能正確的運行的時候,整個操做系統都不能運行,處於一種凍結的狀態。用戶只能無奈的按下Reset按鈕來進行重啓。這樣會致使以前運行的全部的數據都會丟失。所以,新的內核就被設計出來了。--我只是知識的搬運工算法

  在新的OS內核中,進程實際是應用程序的實例要使用的資源的集合。每一個進行都被賦予了一個虛擬地址空間,確保在一個進程中使用的代碼和數據不會被另一個進行所訪問。而線程的職責是對CPU進行邏輯的虛擬化。編程

線程的開銷                                                                                                                                                     安全

  在建立,銷燬一個線程的過程當中,存在着一些巨大的開銷。這些開銷有時間上,也有空間上面的。數據結構

 線程內核對象(Thread-kernel object多線程

  這種數據結構包含着一組對線程進行描述的屬性,這些屬性通常是用於CPU的調度,例如線程的優先級,等待的時間等等。同時還包括了線程的上下文(Thread Contexgt),這是CPU寄存器集合的內存塊所存放的信息的副本。函數

 線程環境塊(thread environment blockspa

  TEB是在用戶模式中分配的初始化的內存塊。TEB耗用一個內存頁(4KB)。TEB包含着異常處理鏈首。線程進入的每一個try塊都在鏈首插入一個節點;線程推出try塊是,從鏈表中刪除該節點。TEB還包含着線程的「線程本地存儲」數據,以及由GDIOpenGL圖形使用的一些數據結構。操作系統

用戶模式棧(User-model stack線程

  這就是常說的用戶棧,用於存放傳給方法的形參和方法中自定義的實參,已經當前方法返回的時候,線程應該從那個地方開始執行。Windows的默認用戶模式棧的大小爲1M。設計

內核模式棧

  當應用程序代碼想操做系統中的內核模式函數傳遞實參時,還會使用內核模式棧。出於對安全的考慮,全部由應用程序向內核函數傳遞的參數,都會先複製到內核模式棧中。因爲程序不能訪問內核模式棧,因此參數一經複製過去內核模式棧中,程序便不能修改其值。其實這個內核模式棧在功能上跟用戶模式棧的做用是同樣的,都是用於存儲傳給方法的形參,已經方法中自定義的實參,以及函數的返回地址。特別之處就是,應用程序不能修改裏面的值,只能由內核函數進行修改,從而達到了安全。

DLL線程鏈接和線程分離通知

  上面所說的三個開銷都是對於內存的開銷,這個DLL線程鏈接和線程分離通知倒是時間上面的開銷。不過這也是相對的來講的,由於上面的三個開銷,在分配內存,初始化內存過程當中,也必須花費不少的時間。在建立一個新的線程的時候,Windows都會調用進程中加載的全部非託管DLLDllMain方法,並向這個方法傳遞一個DLL_THREAD_ATTCH標誌。相似的,終止線程的時候,也會調用這個DllMain方法,並傳遞一個DLL_THREAD_DETACH標誌。由於有些DLL須要獲取這些通知,才能爲進程中建立/銷燬的每一個線程執行特殊的初始化或者清理操做。事實上,每一個進程都會加載不少非託管的DLL文件,因此初始化或者銷燬一個線程,便須要調用多個的DllMain方法。

 

CPU調度時上下文切換的開銷                                                                                                                      

  上下文切換的開銷主要集中在兩個方面:

  • 把一個線程從CPU中移除,而後根據必定的調度算法,用分派器選擇某個線程,在上下文切換器中進行切換。
  • 若是該切換的線程的代碼和數據還在RAM中,就必須從RAM中讀取數據到CacheCPU寄存器中。

 

不使用ThreadPool而本身建立一個線程的原則                                                                                                 

         通常狀況下,要爲不會阻止其餘線程的相對較短的任務處理多個線程而且不須要對這些任務執行任何特定調度時,使用 ThreadPool 類是一種最簡單的方式。 可是,有多個理由建立您本身的線程:

  • 若是您須要使一個任務具備特定的優先級。
  • 若是您具備可能會長時間運行(並所以阻止其餘任務)的任務。
  • 若是您須要將線程放置到單線程單元中(全部 ThreadPool 線程均處於多線程單元中)。
  • 若是您須要與該線程關聯的穩定標識。 例如,您應使用一個專用線程來停止該線程,將其掛起或按名稱發現它。
  • 若是您須要運行與用戶界面交互的後臺線程,.NET Framework 2.0 版提供了 BackgroundWorker 組件,該組件可使用事件與用戶界面線程的跨線程封送進行通訊。

 

 線程的優先級                                                                                                                                              

  Windows中,線程的優先級是從0(最低)-31(最高)的。通常CPU是根據線程的優先級來調度線程的。若是當前調度的線程的線程的優先級是10,若是忽然線程的就緒隊列中,來了一個優先級爲15的線程,那麼操做系統會強制的進行線程的切換,來立刻調度優先級爲15的線程,這被稱爲搶佔式調度。

  在實際編程中,咱們是看不到這0-31的優先級的,這是由於Windows只是公開了這些優先級的一個抽象。特別要說明的的是,線程的優先級由進程的優先級類和線程的相對優先級來決定。

 

進程優先級類

 

 

 

 

 

相對線程優先級

Idle

Below Normal

Normal

Above Normal

High

Realtime

Time-Cirtical

15

15

15

15

15

31

Highest

6

8

10

12

15

26

Above Normal

5

7

9

11

14

25

Normal

4

6

8

10

13

24

Below Normal

3

5

7

9

12

23

Lowest

2

4

6

8

11

22

Idle

1

1

1

1

1

16

 

  這裏要特別說明的是,Windows爲本身保留了優先級0Realtime範圍,同時CLR也爲本身保留了IdleTime-Cirtical範圍的優先級。程序的線程絕對優先級是由進程優先級類和相對線程優先級所共同決定的,同時咱們不該該去更改進程的優先級。由於在正常狀況下,進程根據其啓動它的進程來分配優先級。大多數進程都是由Windows資源管理器啓動,後者在Normal優先級類中生成他們的全部子進程。因此咱們在Thread.Priority中只有Highest,Above Normal,Noraml,Below Normal,Lowset這幾種,相對應的優先級是678910

 spy++軟件查看Chrome的某個線程spy++軟件查看Chrome的某個線程

圖1 Spy++中查看Chrome的某個線程的信息

前臺線程和後臺線程                                                                                                                                    

  前臺線程和後臺線程的主要區別是,進程中只要存在沒有完成的前臺線程,進程就不會被銷燬。換句話說就是,若是全部的前臺線程都完成了,進程就會被銷燬,即便是存在未完成任務的後臺線程。咱們能夠經過設置Thread.IsBackground來把線程設置爲前臺線程或者是後臺線程。經過ThreadPool(其中ThreadPool.QueueUserWorkItemTimer,Task等等都是經過ThreadTool來實現的)來實現的線程都是後臺線程,經過Thread類來實現的線程都是前臺線程。

相關文章
相關標籤/搜索