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()函數關閉

函數功能:讓線程進入等待轉態,直到條件觸發(所有線程執行結束)。內核對象在運行期間處於未觸發的狀態,直到執行結束。

DWORD WINAPI WaitForMultipleObjects(
    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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM