閱讀199 返回首頁    go 阿裏雲 go 技術社區[雲棲]


核心編程隨筆7——線程調度和優先級

NOTE0——何為上下文切換

每一個線程都有一個上下文後者保存在線程的內核對象中這個上下文反映了線程上一次執行時cpu寄存器的狀態。大約每隔20ms(GetSystemTimeAdjustMent函數第二個參數的返回值),windows都會查看所有當前存在的線程內核對象,這些對象中隻有一些是認為可以調度的。window在可調度的線程內核對象中選擇一個,並將上次保存在線程上下文中的值載入cpu寄存器。這一操作成為上下文切換,可以通過spy++看線程屬性。

說明:怎麼確保某個線程在數據到達串行端口來的1MS之內開始運行。答案:不行。實時操作係統可以。但是window不是實時的。實時操作係統需要對底層的硬件有清楚的了解,從而指導硬盤控製器。鍵盤等的延時。

NOTE1——線程的掛起和恢複

有些線程對象的掛起技術大於0,這意味著該線程已經被掛起。不應該給他調度任何cpu時間。可以通過調用CreateProcess或CreateThread函數並傳入CREATE_SUSPENDED標誌來創建一個被掛起的線程。

可以通過調用ResumeThread函數。傳入調用CreateThread時所返回的線程句柄來實現:

DWORD ResumeThread(Handle hThread);如果成功返回線程前一個掛起計數。否則返回0xffffffff,還可以通過SuspendThread來掛起線程DWORD SuspendThread(Handle hThread);任何線程都可以調用這個函數掛起另一個線程,線程可以將自己掛起。但是他無法自己恢複。實際開發中。應用程序在調用suspendthread時必須小心。因為試圖掛起一個線程時。我們不知道線程在做什麼。例如線程正在分配堆中的內存。線程將鎖定堆。當其他線程要訪問堆的時候。他們的執行將被中止。直到第一個線程恢複。之後又在確切知道目標線程是哪個。而且采用完備的措施避免出現因掛起線程而引起的問或死鎖的時候。調用suspendthread才是安全的。

openThread這個函數將找到線程ID匹配的線程內核對象。並將內核對象的使用計數遞增1,然後返回對象的句柄。有了這個句柄。就可以調用suspendthread或resumethread了

NOTE2——切換到另一個線程

SwitchToThread的函數。如果存在另一個可調度線程。那麼係統會讓此線程運行。調用這個函數時。係統查看是否存在正急需cpu時間的饑餓線程。如果沒有立即返回。如果存在.switchtothread將調用該線程。(該優先級可能比swithtothread的主調線程低),

NOTE3——在實際上下文中談context結構。

係統使用context結構記住線程的狀態。這樣線程在下一次獲得cpu可以運行時。就可以在上次停止處繼續。window實際上允許我們查看線程內核對象的內部。並獲得當前cpu寄存器狀態的集合。為此。隻需要調用GetThreadContext:調用它之前應該先調用suspendthread,否則。係統可能正好獲得調度此線程。這樣一來。線程的上下文和所獲得的信息就不一致了。一個線程實際有兩個上下文。用戶模式和內核模式。GetThreadContext隻能返回用戶模式上下文。

通過setthreadcontext來改變結構中的成員。並把新的寄存器值放回線程的內核對象中。同樣如果要改變哪個線程的上下文。應該要暫停該線程。否則結果無法預料。

#include "windows.h"
#include<TLHELP32.H>
#include "stdio.h"
#include "winnt.h"
void SuspendProcess(DWORD dwProcessId,BOOL fsuspend);
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,
PSTR pszCmdLine, int nCmdShow)
 
{
        HANDLE snapshot;  
        PROCESSENTRY32 processinfo ;  
        processinfo.dwSize=sizeof(processinfo) ;  
        snapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); 
        if(snapshot==NULL) 
             return FALSE;  
             BOOL status= Process32First(snapshot,&processinfo);  
             while(status)  
            {  
                    if(stricmp("notepad.exe",processinfo.szExeFile)==0) 
                         break;  
                         status=Process32Next(snapshot,&processinfo);
            }  
           if(status==0)
           {
                  MessageBox(NULL,"沒有找到你要掛起的進程","提示",MB_OK);
                  return 1;
           }
           CloseHandle(snapshot);
           SuspendProcess(processinfo.th32ProcessID,1);
           SuspendProcess(processinfo.th32ProcessID,0);
           return 1;
}
void SuspendProcess(DWORD dwProcessId,BOOL fsuspend)
{
      HANDLE snapshot;  
      snapshot=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
      THREADENTRY32 te={ sizeof(te)};
      BOOL fOK=Thread32First(snapshot,&te);
      for(;fOK;fOK=Thread32Next(snapshot,&te))
     {
             if(te.th32OwnerProcessID==dwProcessId)
             {
                     typedef HANDLE (__stdcall *OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId);  
                    HMODULE hDll =::LoadLibrary("Kernel32.dll");  
                    OPENTHREAD lpfnOpenThread = (OPENTHREAD)::GetProcAddress(hDll, "OpenThread");  
                    HANDLE hThread = lpfnOpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
                   // HANDLE hThread = lpfnOpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                   if(fsuspend==1)
                  {
                         SuspendThread(hThread);
                         CONTEXT context;
                         context.ContextFlags=CONTEXT_FULL;
                         GetThreadContext(hThread,&context);
                         context.Eip=0x00010000;
                         context.ContextFlags=CONTEXT_CONTROL;
                         SetThreadContext(hThread,&context);
                   }
                   else
                        ResumeThread(hThread);
                   // HANDLE hThread=OpenThread (THREAD_SUSPEND_RESUME,FALSE,te.th32OwnerProcessID);
         }
    }
    CloseHandle(snapshot);
}

NOTE4——優先級編程

調用createprocess中可以在fdwcreate參數中傳入需要的優先級。一旦進程運行。可以通過setpriorityclass來改變自己的優先級

SetPriorityClass進行設置

SetPriorityClass

設置優先權

函數原型:

BOOL SetPriorityClass(  HANDLE hProcess,  DWORD dwPriorityClass);

GetPriorityClass

得到優先權

函數原型:

DWORD GetPriorityClass(
  HANDLE hProcess
);

dwPriorityClass:

ABOVE_NORMAL_PRIORITY_CLASS        0x00008000
BELOW_NORMAL_PRIORITY_CLASS      0x00004000
HIGH_PRIORITY_CLASS                             0x00000080

IDLE_PRIORITY_CLASS                              0x00000040
NORMAL_PRIORITY_CLASS                      0x00000020

REALTIME_PRIORITY_CLASS                   0x00000100

例:

if (!CreateProcess("D:\\Program Files\\TTPlayer\\TTPlayer.exe", GetCommandLine(), NULL,
     NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
 {
 
  CloseHandle(pi.hThread);
  WaitForSingleObject(pi.hProcess, INFINITE);
  CloseHandle(pi.hProcess);
 }

BOOL fTemp = SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);
 //BOOL fTemp = SetPriorityClass(pi.hProcess, 21);
 LONG lRet = GetPriorityClass(pi.hProcess);

用來獲取進程優先級的相應函數如下。getpriorityclass

NOTE5

SetProcessPriorityBoost允許或禁止係統提升一個進程中所有線程的優先級。

而SetThreadPriorityBoost則允許或禁止提升某個線程的優先級。有相應的Get函數來判斷當前時候啟用優先級提升。

SetFileInformationByHandle:Sets the file information for the specified file.BOOL WINAPI SetFileInformationByHandle(
  __in  HANDLE hFile,
  __in  FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
  __in  LPVOID lpFileInformation,
  __in  DWORD dwBufferSize
);

係統在啟動時將確定計算機中存在多少個cpu。應用程序可以通過調用GetsystemInfo來查詢機器上cpu的數量如果限製某些進程隻在可用cpu的一個子集上運行。則可以調用SetProcessAffinityMask

The SetProcessAffinityMask function sets a processor affinity mask for the threads of the specified process.


BOOL SetProcessAffinityMask(
  HANDLE hProcess,
  DWORD_PTR dwProcessAffinityMask
);

 
     //get system info
    SYSTEM_INFO SystemInfo;
    GetSystemInfo(
&SystemInfo);

    printf(
" "
        
"dwNumberOfProcessors=%u, dwActiveProcessorMask=%u, wProcessorLevel=%u, "
        
"wProcessorArchitecture=%u, dwPageSize=%u ",
        SystemInfo.dwNumberOfProcessors, SystemInfo.dwActiveProcessorMask, SystemInfo.wProcessorLevel, 
        SystemInfo.wProcessorArchitecture,SystemInfo.dwPageSize
        );
    
if(SystemInfo.dwNumberOfProcessors <= 1return;

    DWORD dwMask 
= 0x0000;
    DWORD dwtmp 
= 0x0001;
    
int nProcessorNum = 0;
    
for(int i = 0; i < 32; i++)
    
...{
        
if(SystemInfo.dwActiveProcessorMask & dwtmp)
        
...{
            nProcessorNum
++;
            
if(nProcessorNum <= 2)
            
...{
                
//如果係統中有多個處理器,則選擇第二個處理器
                dwMask = dwtmp;
            }

            
else
            
...{
                
break;
            }

        }


        dwtmp 
*= 2;

    }
//end of for

    
//進程與指定cpu綁定
    SetProcessAffinityMask(GetCurrentProcess(), dwMask);
    
//線程與指定cpu綁定
    
//SetThreadAffinityMask(GetCurrentThread(),dwMask);

    
return ;

 

 SetThreadAffinityMask是設置此線程隻能在某個或某些處理器上運行;SetThreadIdealProcessor是在上述設置的範圍內,指定線程優先在某個處理器上運行

 當vista在x86計算機上啟動時。我們可以限製係統將所有的cpu數量。在啟動過程中。係統將檢查啟動配置數據(BCD),BCD是一個取代老的boot.ini文本文件的數據存庫,他在計算機的硬件和固件之上提供了一個抽象層。BCD的編程配置是通過wmi實現的。

最後更新:2017-04-03 15:21:57

  上一篇:go [曆年IT筆試題]2014美團網校園招聘筆試題(長沙站)
  下一篇:go 如何將報表中的參數傳遞給VB