CreateThread的用法及在Qt等GUI開發中使用CreateThread的一些技巧


Qt中有自己的多線程工具QThread,但是每次都需要繼承QThread實現一個自己的類,然后重載其中的run()成員函數,工作量比較大。有的時候只需要執行一個運行時間長的函數來更新GUI及后台的一些數據,如果直接寫在GUI更新所在的主線程中,就會容易導致線程假死。因此,對於運行時間比較長的代碼,我們需要創建新的線程來執行它們。使用QThread是一種方法,另外一種,對於Windows平台下的開發,可以用Windows api里的CreateThread函數。

CreateThread的一個簡單例子如下:

//最簡單的創建多線程實例  
#include <stdio.h>
#include <windows.h>
//子線程函數
DWORD WINAPI ThreadFun(LPVOID pM)
{
printf("子線程的線程ID號為:%d\n子線程輸出Hello World\n", GetCurrentThreadId());
return 0;
}
//主函數,所謂主函數其實就是主線程執行的函數。
int main()
{
printf(" 最簡單的創建多線程實例\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

HANDLE handle = CreateThread(NULL, 0, ThreadFun, NULL, 0, NULL);
WaitForSingleObject(handle, INFINITE);
return 0;
}

下面來細講下代碼中的一些函數

第一個 CreateThread

函數功能:創建線程

函數原型:

HANDLEWINAPICreateThread(

  LPSECURITY_ATTRIBUTESlpThreadAttributes,

  SIZE_TdwStackSize,

  LPTHREAD_START_ROUTINElpStartAddress,

  LPVOIDlpParameter,

  DWORDdwCreationFlags,

  LPDWORDlpThreadId

);

函數說明:

第一個參數表示線程內核對象的安全屬性,一般傳入NULL表示使用默認設置。

第二個參數表示線程棧空間大小。傳入0表示使用默認大小(1MB)。

第三個參數表示新線程所執行的線程函數地址,也就是放在這個線程里執行的用戶函數的地址。多個線程可以使用同一個函數地址。

第四個參數是傳給線程函數的參數。

第五個參數指定額外的標志來控制線程的創建,為0表示線程創建之后立即就可以進行調度,如果為CREATE_SUSPENDED則表示線程創建后暫停運行,這樣它就無法調度,直到調用ResumeThread()

第六個參數將返回線程的ID號,傳入NULL表示不需要返回該線程ID號。

函數返回值:

成功返回新線程的句柄,失敗返回NULL。 

 

第二個 WaitForSingleObject

函數功能:等待函數 – 使線程進入等待狀態,直到指定的內核對象被觸發。

函數原形:

DWORDWINAPIWaitForSingleObject(

  HANDLEhHandle,

  DWORDdwMilliseconds

);

函數說明:

第一個參數為要等待的內核對象。

第二個參數為最長等待的時間,以毫秒為單位,如傳入5000就表示5秒,傳入0就立即返回,傳入INFINITE表示無限等待。

因為線程的句柄在線程運行時是未觸發的,線程結束運行,句柄處於觸發狀態。所以可以用WaitForSingleObject()來等待一個線程結束運行。

函數返回值:

在指定的時間內對象被觸發,函數返回WAIT_OBJECT_0。超過最長等待時間對象仍未被觸發返回WAIT_TIMEOUT。傳入參數有錯誤將返回WAIT_FAILED


CreateThread()函數是Windows提供的API接口,在C/C++語言另有一個創建線程的函數_beginthreadex(),在很多書上(包括《Windows核心編程》)提到過盡量使用_beginthreadex()來代替使用CreateThread()

_beginthreadex()函數在創建新線程時會分配並初始化一個_tiddata塊。這個_tiddata塊自然是用來存放一些需要線程獨享的數據。事實上新線程運行時會首先將_tiddata塊與自己進一步關聯起來。然后新線程調用標准C運行庫函數如strtok()時就會先取得_tiddata塊的地址再將需要保護的數據存入_tiddata塊中。這樣每個線程就只會訪問和修改自己的數據而不會去篡改其它線程的數據了。因此,如果在代碼中有使用標准C運行庫中的函數時,盡量使用_beginthreadex()來代替CreateThread()

類似於上面的程序用CreateThread()創建輸出“Hello World”的子線程,下面使用_beginthreadex()來創建多個子線程:

//子線程報數  
#include <stdio.h>
#include <process.h>
#include <windows.h>
int g_nCount;
//子線程函數
unsigned int __stdcall ThreadFun(PVOID pM)
{
g_nCount++;
printf("線程ID號為%4d的子線程報數%d\n", GetCurrentThreadId(), g_nCount);
return 0;
}
//主函數,所謂主函數其實就是主線程執行的函數。
int main()
{
printf(" 子線程報數 \n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");

const int THREAD_NUM = 10;
HANDLE handle[THREAD_NUM];

g_nCount = 0;
for (int i = 0; i < THREAD_NUM; i++)
handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
return 0;
}


在Qt中如何使用CreateThread來執行運行時間較長的代碼來更新GUI和后台數據呢?可以參考 C++靜態成員函數訪問非靜態成員變量的方法這篇文章。










注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com