1. 程式人生 > 實用技巧 >Windows多執行緒與執行緒繫結CPU核心

Windows多執行緒與執行緒繫結CPU核心

一、Windows建立多執行緒的方法有CreadThread()和_beginthreadex()函式,Win32 提供了一系列的API函式來完成執行緒的建立、掛起、恢復、終結以及通訊等工作,標頭檔案在

#include<windows.h>

先介紹一下CreateThread()主要的函式列表

CreateThread()函式原型

 HANDLE WINAPI CreateThread(
    _In_opt_  LPSECURITY_ATTRIBUTES  //lpThreadAttributes, 執行緒核心物件的安全屬性,一般傳入NULL表示使用預設設定。  
_In_ SIZE_T //dwStackSize,表示執行緒棧空間大小。傳入0表示使用預設大小(1MB) _In_ LPTHREAD_START_ROUTINE //lpStartAddress,表示新執行緒所執行的執行緒函式地址,多個執行緒可以使用同一個函式地址 _In_opt_ LPVOID //lpParameter,是傳給執行緒函式的引數 _In_ DWORD //dwCreationFlags,指定額外的標誌來控制執行緒的建立,為0表示執行緒建立之後立即就可以進行排程,如果為CREATE_SUSPENDED則表示執行緒建立後暫停執行,這樣它就無法排程,直到呼叫ResumeThread()恢復執行
_Out_opt_ LPDWORD //lpThreadId將返回執行緒的ID號,傳入NULL表示不需要返回該執行緒ID號 );

CreateThread()函式的返回值

執行緒建立成功返回新執行緒的控制代碼,失敗返回NULL

執行緒等待函式(關閉執行緒)

CreateThread()建立的函式不會隨著程式結束自動關閉,需要自己呼叫WaitForMultipleObjects()函式關閉

函式功能:讓執行緒進入等待轉態,直到條件觸發(所有執行緒執行結束)。核心物件在執行期間處於未觸發的狀態,直到執行結束。

DWORDWINAPIWaitForMultipleObjects(
  DWORD nCount,       
//CPU核心物件的個數 CONST HANDLE *lpHandles, //控制代碼陣列的地址 BOOL bWaitAll, //是否等待所有執行緒結束 DWORD dwMilliseconds //等待的最大時間,單位毫秒,INFINITE表示無限等待 );

二、_beginthreadex()函式的引數和CreateThread()函式一樣,都是六個,_beginthreadex()是C/C++語言另有一個建立執行緒的函式,我們應該儘量使用_beginthreadex()來代替使用CreateThread(),因為它比CreateThread()更安全。

_beginthreadex建立的每個執行緒都將擁有自己專用的一塊記憶體區域來供標準C執行庫中所有有需要的函式使用。而且這塊記憶體區域的建立就是由C/C++執行庫函式_beginthreadex()來負責的。這塊區域可以用來存放一些執行緒獨享的資料,不會被其它執行緒修改,所以更安全。

三、多執行緒繫結CPU核心

線上程數和CPU核心數大致相等的情況下,將執行緒與CPU核心繫結,可以減少執行緒的上下文切換帶來的開銷,提高CPU Cache快取的命中率,提高執行效率

Windows是使用SetThreadAffinityMask(handle[i], 1 << i);函式來將執行緒與CPU核心繫結的,第一個引數handle[ i ] 是執行緒的控制代碼,第二個引數代表CPU核心的編號

SetThreadAffinityMask()函式的返回非零值表示繫結CPU核心成功,為零值表示失敗

執行緒的控制代碼可以通過CreateThread()建立執行緒的返回值獲取,也可以使用GetCurrentThread()函式的返回值獲取當前執行執行緒的控制代碼

注意CPU的核心編號是按照二進位制位來表示的,每個二進位制位指向一個核心

比如對4核的CPU

第一個核編號是0x0001

第二個核編號是0x0010

第三個核編號是0x0100

第四個核編號是0x1000

#include<process.h>
#include<windows.h>
#include<thread>
#include<iostream>
using namespace std;

//CreateThread()核_beginthreadex()的執行緒函式要求是全域性變數的一個函式
unsigned int __stdcall ThreadFun(PVOID pM)
{
    printf("執行緒ID 為 %d 的子執行緒輸出: Hello World\n", GetCurrentThreadId());
    while (1) {

    }
    return 0;
}


int main()
{
    //獲取CPU核心數目
    int x = thread::hardware_concurrency();
    const int THREAD_NUM = 4;
    HANDLE handle[THREAD_NUM];
    unsigned long mask;
    for (int i = 0; i < THREAD_NUM; i++)
    {
        //CREATE_SUSPENDED建立執行緒之後掛起
        handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, CREATE_SUSPENDED, NULL);

        //mask值為0表示設定失敗,非零值表示成功
        mask=SetThreadAffinityMask(handle[i], 1 << i);
        if(mask==0)
            printf("ERROR\n");
    }
    int n, t = 4;
    while (t--) {
        cin >> n;
        //喚醒控制代碼為handle[n]的執行緒
        ResumeThread(handle[n]);
    }
    
    //等待所有執行緒退出
    WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
    system("pause");
    return 0;
}

喚醒執行緒執行之前CPU使用狀況

喚醒控制代碼為0的執行緒執行

喚醒控制代碼為1的執行緒執行

喚醒控制代碼為3的執行緒執行

喚醒控制代碼為2的執行緒執行

參考部落格

https://www.cnblogs.com/ay-a/p/8762951.html

https://blog.csdn.net/zhangxiangdavaid/article/details/43700485