线程调度优先级和关联性
每個現場都有一個上下文。后者保存在線程的內核對象中。這上下文反應了線程上一次執行時CPU寄存器的狀態,大約每隔20ms(GetSystemTimeAdjustment函數第二個參數的返回值),windows都會查看所有當前存在的線程內核對象,在這些對象中,只有一些被認為是可以調度的,windows在可調度的線程內核對象中選擇一個,并將上次保存在線程上下文中的值載入CPU寄存器,這一操作被稱為上下文切換。
在縣城初始化之后,CreateProcess或者CreateThread函數將查看是否有CREATE_SUSPENDED標志傳入,如果有,函數會返回并讓新線程處于掛起狀態。如果沒有函數將線程的掛起計數遞減為0,當線程的掛起計數為0時,線程就成為可調度的,除非還在等待某個事件 的發生。通過創建一個處于掛起狀態的線程,我們可以通過ResumeThread來恢復線程。同樣也有SuspendThread可以掛起線程。
線程可以告訴系統要休眠,調用Sleep函數
1.調用Sleep函數將使得線程資源放棄屬于他的時間片中剩下的部分
2.系統設置線程不可調度的時間只是近似于所設定的毫秒數,windows不是實時操作系統,線程實際醒來的情況取決于系統中其他線程的運行情況
3.可以調用Sleep函數并給dwms參數傳入INFINITE,這是在告訴系統,永遠不要調度這進程
4.Sleep(0),這是在告訴系統,主調線程放棄了時間片的剩余部分,它強制系統調度其他線程,但是系統有可能重新調低剛剛調用了sleep(0)的線程,如果沒有相同或者較高優先級的可調度線程時,就會發生這樣的情況。
切換到另一個線程
系統提供了一個名為SwitchToThread的函數,如果存在另一個可調度的線程,那么系統會讓此線程運行。
Bool SwitchToThread();
調用這函數時,系統查看是否存在正急需CPU時間的饑餓線程,如果沒有,函數立即返回,如果存在,則調度該線程(其優先級可能比SwitchToThread的主調線程低),饑餓線程運行一個時間量,然后系統調度程序回復正常運行。
通過調用SwitcgToThread和調用Sleep(0)類似,區別在于SwitchToThread運行執行較低優先級的線程。
線程的執行時間
有時候我們需要計算一個線程執行某項任務需要消耗多長時間,關于這一點,可以利用GetTickCount64();
但是有一個前提就是代碼的執行不會被中斷,可是在搶占式的操作系統,這樣是比較困難的。
BOOL WINAPI GetThreadTimes(_In_ HANDLE hThread,//需要獲取cpu時間的線程句柄_Out_ LPFILETIME lpCreationTime,//創建時間_Out_ LPFILETIME lpExitTime,//退出時間_Out_ LPFILETIME lpKernelTime,//內核時間_Out_ LPFILETIME lpUserTime//用戶時間 );同樣的也有GetProcessTimes,可以用于進程中的所有線程。
BOOL WINAPI GetProcessTimes( __in HANDLE hProcess, __out LPFILETIME lpCreationTime, __out LPFILETIME lpExitTime, __out LPFILETIME lpKernelTime, __out LPFILETIME lpUserTime ); hProcess:為需要獲取相關時間的進程句柄lpCreationTime:進程的創建時間
lpExitTime:進程的退出時間
lpKernelTime:進程在內核模式下的所有時間
lpUserTime:進程在用戶模式下的所有時間 BOOL QueryPerformanceFrequency(LARGE_INTEGER *pliFrequency) BOOL QueryPerformanceCounter(LARGE_INTEGER *pliCount);
這兩個函數假設正在執行的線程不會被搶占。它們都是針對生命期很短的代碼塊。GetCPUFrequencyInMHZ可以獲得cpu頻率。
在windows定義的所有數據結構中,CONTEXT結構是唯一一個依賴于cpu的。我們可以通過調用GetThreadContext來獲得當期cpu寄存器的狀態。
hThread:獲取信息目標進程的線程句柄。(用OpenThread獲取)
lpContext:一個用于接收信息的CONTEXT結構指針
返回值:如果成功,返回值不為零,如果不成功,返回值為零,若想看其他的錯誤信息,參考GetLastError
第二個參數是CONTEXT結構指針。在分配CONTEXT結構后,需要初始化ContextFlag標志,表示以表示要獲取哪些寄存器。函數執行后CONTEXT對象中就填入我們請求的成員。
ContextFlag可以是:
CONTEXT_CONTROL表示控制寄存器。
CONTEXT_INTEGER表示整數寄存器。
CONTEXT_FLOAT?表示浮點寄存器。
CONTEXT_ALL?表示CONTEXT_CONTROL?|CONTEXT_INTEGER|CONTEXT_SEGMENTS。
?在調用GetThreadContext時,需要先調用SuspendThread。因為在調用GetThreadContext時系統可能正在執行那個線程,此時線程的上下文與獲得的信息就不一致了。注意,它只能返回線程的用戶模式上下文。如果當調用SuspendThread時線程正在內核模式運行,線程不會暫停,直到其返回用戶空間。但是返回到用戶控件后不會執行任何用戶模式代碼。
不僅僅能獲得線程的進程上下文,我們還可以設置它。這可以調用:
?BOOL SetThreadContext( HANDLE hThread,CONST CONTEXT *pContext);
GetThreadContext和SetThreadContext函數為我們提供了對線程許多控制的方法,但是需要小心使用。
線程優先級
前面提到的調度程序在調度另外一個線程之前,可以運行一個線程大約20ms的時間。但是這是所有優先級都相同的情況。實際上系統中的很多線程優先級是不同的,這將影響調度程序如何選擇下一個要運行的線程。
Windows的線程優先級從0到31。每個線程都會分配一個優先級。當系統確定給哪個線程分配cpu時,它會首先查看優先級為31的線程,直至所有優先級為31的線程都被調度。然后再查看下一優先級線程。只要存在優先級為31的線程,系統就不會調度0-30級的線程。低優先級線程長時間得不到cpu時間,這被稱為饑餓。這不經常出現,因為大多數線程都是不可調度的。
系統啟動時會創建一個優先級為0的idle線程,整個系統只有它的優先級為0。它在系統中沒有其他線程運行時將系統內存中所有閑置頁面清0。
Windows中的線程優先級是由優先級類和相對線程優先級來確定的。系統通過線程的相對優先級加上線程所屬進程的優先級來確定線程的優先級值。這個值被稱為線程的基本優先級值。
Windows支持6個進程優先級類:idle?,below?normal?,normal?,above?normal,high和real-time。它們是相對與進程的。Normal最為常用,為99%的進程使用。
idle優先級類在系統什么都不做的時候運行的應用程序。如屏幕保護程序。real-time優先級類優先級別最高,但是沒有開放給用戶使用。因為此優先級類的程序會影響操作系統的任務。
Windows支持7個相對線程優先級:idle,lowest?,below?normal,normal,above?normal,highest和time-critical。這些優先級是相對于進程優先級的。大多數的線程使用normal優先級。
概括起來就是進程屬于某個優先級類,另外還可以指定進程中線程的相對線程優先級。也就是說線程優先級是相對于進程優先級的。time-critical優先級對于real-time優先級類,優先級為31。相對于其他優先級類則為15。需要注意的是進程優先級是抽象的概念,因為進程并不參與調度。
在優先級編程時,首先需要在調用CreateProcess時可以再fdwCreate參數中傳入想要的優先級。fdwCreate可以是以下標識符:
real-time????????REALTIME_PRIORITY_CLASS
high????????????HIGH_PRIORITY_CLASS
above?normal?????ABOVE_NORMAL_PRIORITY_CLASS
normal??????????NORMAL_PRIORITY_CLASS
below_normal????BELOW_NORMAL_PRIORITY_CLASS
idle?????????????IDLE_PRIORITY_CLASS
進程運行后可以調用SetPrioritClass來改變進程優先級類。
BOOL SetPriorityClass(HANDLE hProcess,DWORD fdwPriority);
可以調用GetPriorityClass來獲得進程的優先級類。DWORD?GetPriorityClass(HANDLE?hProcess);
上面是指定的進程優先級類,調用CreateThread創建線程時,它的線程優先級總是被設置為normal.
關聯性
????默認情況下,windows在分配cpu時采用軟關聯的方式。也就是說在其他因素相同的情況下,系統使線程在上一次運行的處理器上運行。這有助于重用仍在處理器高速緩存中的數據。
系統在啟動時確定cpu數量。應用程序可以通過調用GetSysInfo來查詢cpu的數量。如果需要限制一個進程的所有線程在某些cpu上運行,可以調用:
第二參數是一個位掩碼。代表線程可以在哪些cpu上運行。
注意子進程將繼承父進程的關聯性。
GetProcessAffinityMask返回進程的關聯掩碼。
相應的還可以設置某個線程只在一組cpu上運行:
SetThreadAffinityMask。
有時候強制一個線程只在某個特定的cpu上運行并不是什么好主意。Windows允許一個線程運行在一個cpu上,但如果需要,它將被移動到一個空閑的cpu上。
要給線程設置一個理想的cpu,可以調用: DWORD SetThreadIdealProcessro(HANDLE hThread,DWORD dwIdealProcessor); dwIdealProcessor是一個0到31/63之間的整數。表示線程希望設置的cpu。可以傳入MAXIMUM_PROCESSOR值,表示沒有理想的cpu。
總結
以上是生活随笔為你收集整理的线程调度优先级和关联性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud中,Eureka
- 下一篇: SQL--Chapter1_Overvi