CRT: (C Runtime Library)即C運(yùn)行時(shí)庫(kù),是系統(tǒng)運(yùn)行的基礎(chǔ),包含了c常用的函數(shù)集(如:printf,malloc,strcpy等),為運(yùn)行main做了初始化環(huán)境變量、堆、io等資源,并在結(jié)束后清理。
在Windows環(huán)境下,VC提供的 C run-time library又分為動(dòng)態(tài)運(yùn)行時(shí)庫(kù)、靜態(tài)運(yùn)行時(shí)庫(kù)、多線(xiàn)程、單線(xiàn)程、調(diào)試版本(Debug)、發(fā)行版本(Release)等。
開(kāi)關(guān) | 對(duì)應(yīng)的庫(kù) | 版本 |
/MD | MSVCRT.LIB | 多線(xiàn)程DLL的Release版本 |
/MDd | MSCVRTD.LIB | 多線(xiàn)程DLL的Debug版本 |
/MT | LIBCMT.LIB | 多線(xiàn)程靜態(tài)鏈接的Release版本 |
/MTd | LIBCMTD.LIB | 多線(xiàn)程靜態(tài)鏈接的Debug版本 |
/clr | MSVCMRT.LIB | 托管代碼和非托管代碼混合 |
/clr:pure | MSVCURT.LIB | 純托管代碼 |
圖1:編譯器運(yùn)行庫(kù)設(shè)置(VS2008)
具體請(qǐng)參考MSDN:
ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_vccrt/html/a889fd39-807d-48f2-807f-81492612463f.htm
序號(hào) | 函數(shù)名 | 功能 |
1 | _beginthread() | 創(chuàng)建一個(gè)新線(xiàn)程 |
2 | _endthread() | 結(jié)束一個(gè)線(xiàn)程的執(zhí)行 |
3 | _beginthreadex() | 創(chuàng)建一個(gè)新線(xiàn)程 |
4 | _endthreadex() | 結(jié)束一個(gè)線(xiàn)程的執(zhí)行 |
5 | ResumeThread() | 恢復(fù)線(xiàn)程的運(yùn)行 |
6 | SuspendThread() | 掛起線(xiàn)程 |
7 | GetExiCodeThread() | 得到一個(gè)線(xiàn)程的退出碼 |
8 | WaitForSingleObject() | 等待單個(gè)對(duì)象 |
9 | WaitForMultipleObjects() | 等待多個(gè)對(duì)象 |
3.1、_beginthread和_endthread
該函數(shù)是C Runtime Library中的函數(shù)。其原型如下
unsigned long _beginthread(
void( __cdecl *start_address )( void * ),//線(xiàn)程函數(shù)的起始地址
unsigned stack_size,//堆棧大小,設(shè)置0為系統(tǒng)默認(rèn)值
void *arglist );//傳遞給線(xiàn)程函數(shù)的參數(shù),沒(méi)有則為NULL
“該函數(shù)被認(rèn)為是頭腦簡(jiǎn)單的函數(shù)”,使用該函數(shù)導(dǎo)致無(wú)法有效的控制被創(chuàng)建線(xiàn)程,如不能在啟動(dòng)時(shí)將該線(xiàn)程掛起,無(wú)法為該線(xiàn)程設(shè)置優(yōu)先權(quán)等。另外,無(wú)法利用這個(gè)Handle來(lái)等待該線(xiàn)程結(jié)束等操作。該函數(shù)是早期的C Runtime Library的產(chǎn)物,不提倡使用,后期的改良版本為_(kāi)beginthreadex。
通過(guò)_beginthread啟動(dòng)的線(xiàn)程在應(yīng)當(dāng)通過(guò)調(diào)用_endthread結(jié)束,以保證清除與線(xiàn)程相關(guān)的資源。_endthread的原型為:
void _endthread(void);
3.2、_beginthreadex和_endthreadex
該函數(shù)是C Runtime Library中的一個(gè)函數(shù),用標(biāo)準(zhǔn)C實(shí)現(xiàn),相比_beginthread,_beginthreadex對(duì)線(xiàn)程控制更為有力(比前者多三個(gè)參數(shù)),是_beginthread的加強(qiáng)版。其原型為:
unsignedlong _beginthreadex(
void *security,//線(xiàn)程函數(shù)的安全描述符
unsigned stack_size,// 堆棧大小,設(shè)置0為系統(tǒng)默認(rèn)值
unsigned ( __stdcall *start_address )( void * ),//線(xiàn)程函數(shù)的起始地址
void*arglist, //傳遞給線(xiàn)程函數(shù)的參數(shù),沒(méi)有則為NULL
unsignedinitflag,//初始狀態(tài),0為立即執(zhí)行,CREATE_SUSPEND為創(chuàng)建后掛起
unsigned*thrdaddr );//指向一個(gè)32為的變量,存放線(xiàn)程標(biāo)識(shí)符
該函數(shù)返回新線(xiàn)程的句柄,通過(guò)該句柄可實(shí)現(xiàn)對(duì)線(xiàn)程的控制。雖然,該函數(shù)是用標(biāo)準(zhǔn)C寫(xiě)的(即可不加修改就可以移植到其他系統(tǒng)執(zhí)行),但是由于它與Windows系統(tǒng)有著緊密的聯(lián)系(需要手動(dòng)關(guān)閉該線(xiàn)程產(chǎn)生的Handle),因此實(shí)現(xiàn)時(shí),往往需要包含windows.h。
通過(guò)_beginthreadex啟動(dòng)的線(xiàn)程通過(guò)調(diào)用_endthreadex做相關(guān)清理。該函數(shù)比較像CreateThread函數(shù)。
_endthreadex函數(shù)的原型為:
void _endthreadex(unsigned retVal);
關(guān)于這兩組函數(shù)的詳細(xì)區(qū)別請(qǐng)參考MSDN的說(shuō)明:
Creates a thread.
uintptr_t _beginthread( void( *start_address )( void * ), unsigned stack_size, void *arglist ); uintptr_t _beginthreadex( void *security, unsigned stack_size, unsigned ( *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr ); |
start_address
Start address of a routine that begins execution of a new thread.For _beginthread, the calling convention is either__cdecl or __clrcall; for _beginthreadex, it iseither __stdcall or __clrcall.
stack_size
Stack size for a new thread or 0.
arglist
Argument list to be passed to a new thread or NULL.
security
Pointer to a SECURITY_ATTRIBUTES structure that determines whetherthe returned handle can be inherited by child processes. If NULL, the handlecannot be inherited. Must be NULL for Windows 95 applications.
initflag
Initial state of a new thread (0 forrunning or CREATE_SUSPENDED for suspended); useResumeThread to execute the thread.
thrdaddr
Points to a 32-bit variable that receives the thread identifier.Might be NULL, in which case it is not used.
If successful, each of these functions returns a handle to thenewly created thread; however, if the newly created thread exits too quickly, _beginthread might not return a valid handle (see thediscussion in the Remarks section). _beginthread returns -1L on an error, in which case errno is set to EAGAIN if thereare too many threads, to EINVAL if the argument isinvalid or the stack size is incorrect, or to EACCESin the case of insufficient resources (such as memory). _beginthreadexreturns 0 on an error, in which case errno and _doserrno are set.
If startaddress is NULL, the invalid parameter handler is invoked, asdescribed inParameter Validation. If execution is allowed to continue, these functions set errno to EINVAL and return -1.
For more information about these and other return codes, see_doserrno, errno, _sys_errlist, and _sys_nerr.
For more information about uintptr_t,see Standard Types.
The _beginthread function creates athread that begins execution of a routine at start_address.The routine at start_address must use the __cdecl calling convention and should have no returnvalue. When the thread returns from that routine, it is terminatedautomatically. For more information about threads, see Multithreading.
_beginthreadex resembles the Win32 CreateThread API more closely than _beginthread does. _beginthreadexdiffers from _beginthread in the following ways:
· _beginthreadex has three additional parameters: initflag,security, and threadaddr.The new thread can be created in a suspended state, with a specified security(Windows NT only), and can be accessed using thrdaddr,which is the thread identifier.
· The routine at start_address passed to _beginthreadexmust use the __stdcall calling convention and mustreturn a thread exit code.
· _beginthreadex returns 0 on failure, rather than -1L.
· A thread created with _beginthreadex is terminated by a call to _endthreadex.
The _beginthreadex function gives you morecontrol over how the thread is created than _beginthreaddoes. The _endthreadex function is also moreflexible. For example, with _beginthreadex, you canuse security information, set the initial state of the thread (running orsuspended), and get the thread identifier of the newly created thread. You arealso able to use the thread handle returned by _beginthreadexwith the synchronization APIs, which you cannot do with _beginthread.
It is safer to use _beginthreadex than _beginthread. If the thread generated by _beginthread exits quickly, the handle returned to thecaller of _beginthread might be invalid or, worse,point to another thread. However, the handle returned by _beginthreadexhas to be closed by the caller of _beginthreadex, soit is guaranteed to be a valid handle if _beginthreadexdid not return an error.
You can call _endthread or _endthreadex explicitly to terminate athread; however, _endthread or _endthreadexis called automatically when the thread returns from the routine passed as aparameter. Terminating a thread with a call to endthreador _endthreadex helps to ensure proper recovery ofresources allocated for the thread.
_endthread automatically closes the thread handle (whereas _endthreadex does not). Therefore, when using _beginthread and _endthread, donot explicitly close the thread handle by calling the Win32 CloseHandle API.This behavior differs from the Win32 ExitThread API.
Note: |
For an executable file linked with Libcmt.lib , do not call the Win32 ExitThread API; this prevents the run-time system from reclaiming allocated resources. _endthread and _endthreadex reclaim allocated thread resources and then call ExitThread. |
The operating system handles the allocation of the stack wheneither _beginthread or _beginthreadexis called; you do not need to pass the address of the thread stack to either ofthese functions. In addition, the stack_sizeargument can be 0, in which case the operating system uses the same value asthe stack specified for the main thread.
arglist is a parameter to be passed to the newly created thread.Typically it is the address of a data item, such as a character string. arglist can be NULL if it isnot needed, but _beginthread and _beginthreadex must be provided with some value to pass tothe new thread. All threads are terminated if any thread calls abort, exit, _exit, or ExitProcess.
The locale of the new thread is inherited from its parent thread.If per thread locale is enabled by a call to _configthreadlocale (eitherglobally or for new threads only), the thread can change its localeindependently from its parent by calling setlocaleor _wsetlocale. For more information, see Locale.
For mixed and pure code, _beginthreadand _beginthreadex both have two overloads, onetaking a native calling-convention function pointer, the other taking a __clrcall function pointer. The first overload is notapplication domain-safe and never will be. If you are writing mixed or purecode you must ensure that the new thread enters the correct application domainbefore it accesses managed resources. You can do this, for example, by usingcall_in_appdomain Function. The second overload is application domain-safe; thenewly created thread will always end up in the application domain of the callerof _beginthread or _beginthreadex.
Routine | Required header |
_beginthread | <process.h> |
_beginthreadex | <process.h> |
For more compatibility information, see Compatibility in theIntroduction.
Multithreaded versions of the C run-time libraries only.
_beginthread()和_beginthreadex()的線(xiàn)程執(zhí)行函數(shù)的定義是不一樣的。
對(duì)于_beginthread()創(chuàng)建的線(xiàn)程,其線(xiàn)程函數(shù)定義為:
void ThreadPro(void * pArguments );
對(duì)于_beginthreadex()創(chuàng)建的線(xiàn)程,其線(xiàn)程函數(shù)定義為:
unsigned __stdcallThreadFunc( void* pArguments )
(1)兩者創(chuàng)建線(xiàn)程函數(shù)方式不同,_beginthreadex()的線(xiàn)程函數(shù)必須使用__stdcall調(diào)用方式,而且必須返回一個(gè)unsigned型的退出碼。
(2)_beginthreadex()在創(chuàng)建線(xiàn)程失敗時(shí)返回0,而_beginthread()在創(chuàng)建線(xiàn)程失敗時(shí)返回-1,這一點(diǎn)在檢測(cè)返回結(jié)果時(shí)必須注意。
(3)如果是調(diào)用_beginthread()創(chuàng)建線(xiàn)程,并相應(yīng)地調(diào)用_endthread()結(jié)束線(xiàn)程時(shí),系統(tǒng)將自動(dòng)關(guān)閉線(xiàn)程句柄。而調(diào)用_beginthreadex()創(chuàng)建線(xiàn)程,并相應(yīng)地調(diào)用_endthreadex()結(jié)束線(xiàn)程時(shí),系統(tǒng)不能自動(dòng)關(guān)閉線(xiàn)程句柄。
(4)由于_beginthread()創(chuàng)建線(xiàn)程參數(shù)比較簡(jiǎn)單,不能控制線(xiàn)程的初始啟動(dòng)狀態(tài),且不返回創(chuàng)建的線(xiàn)程句柄,也不能調(diào)用
WaitForSingleObject()/WaitForMultipleObjects()函數(shù)。所以一般不常用,而_beginthreadex()與CreatThread()函數(shù)比較相似。能方便控制線(xiàn)程。
實(shí)例說(shuō)明:該實(shí)例主要使用_beginthread()函數(shù),創(chuàng)建盡可能多的線(xiàn)程,知道系統(tǒng)不能再創(chuàng)建為止。注意實(shí)時(shí)數(shù)據(jù)的顯示和參數(shù)的傳遞,如果想傳遞多個(gè)參數(shù),則可以使用結(jié)構(gòu)體。
主要函數(shù)如下:
//在.h文件中:
#defineWM_ADD WM_USER+10//定義消息
voidThreadFunc1(void *pArg);//線(xiàn)程函數(shù)1:開(kāi)始創(chuàng)建
voidThreadFunc2(void *pArg);//線(xiàn)程函數(shù)2:
LRESULTOnMyMsg(WPARAM,LPARAM);//消息函數(shù)
//在.cpp文件中:
//全局變量,一般在cpp中定義,不要在.h定義,否則編譯出錯(cuò):變量重復(fù)定義
boolg_bRun = false;
longg_nCount = 0;
//開(kāi)始按鈕函數(shù)
voidC_beginthreadDlg::OnBnClickedButtonRun()
{
// TODO:在此添加控件通知處理程序代碼
//創(chuàng)建線(xiàn)程:主要是用來(lái)創(chuàng)建執(zhí)行創(chuàng)建線(xiàn)程池的線(xiàn)程
if (_beginthread(ThreadFunc1,0,&m_hWnd) != -1 )
{//成功
GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);
}
}
//創(chuàng)建線(xiàn)程池的線(xiàn)程函數(shù)
voidThreadFunc1(void *pArg)
{
HWND *hWnd = (HWND*)pArg;//得到窗口句柄
g_nCount= 0;//初始化變量
g_bRun =true;
while (g_bRun)
{//開(kāi)始創(chuàng)建盡可能多的線(xiàn)程
if (_beginthread(ThreadFunc2,0,hWnd) == -1)
{//失敗則結(jié)束
g_bRun= false;
break;
}
}
//退出
::SendMessage(*hWnd,WM_ADD,1,0);
}
//線(xiàn)程函數(shù)
voidThreadFunc2(void *pArg)
{
HWND *hWnd = (HWND*)pArg;
g_nCount++;
::SendMessage(*hWnd,WM_ADD,0,g_nCount);//顯示個(gè)數(shù)
while(g_bRun)
{
Sleep(1000);
}
}
// WM_ADD消息處理函數(shù)
LRESULTC_beginthreadDlg::OnMyMsg(WPARAMwParam, LPARAMlParam)
{
if (wParam == 1)
{//結(jié)束
GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(TRUE);
}
else
{//顯示更新
m_nCount= g_nCount;
UpdateData(FALSE);
}
return 0;
}
運(yùn)行效果:
圖2:實(shí)例1演示
工程源碼下載地址:
http://download.csdn.net/detail/cbnotes/4902781
歡迎大家修改和指正。
注意事項(xiàng):
(1)由于_beginthread()創(chuàng)建的線(xiàn)程結(jié)束后自動(dòng)關(guān)閉線(xiàn)程句柄,所以不能使用WaitForSingleObject()/WaitForMultipleObjects()函數(shù)來(lái)同步。
(2)上面的程序中,先是創(chuàng)建了線(xiàn)程1(ThreadFunc1),再用該線(xiàn)程1來(lái)創(chuàng)建眾多線(xiàn)程,而不是直接在OnBnClickedButtonRun()函數(shù)中創(chuàng)建眾多線(xiàn)程,大家知道這是為什么嗎?主要是為了能夠實(shí)時(shí)顯示創(chuàng)建的線(xiàn)程數(shù)目。如果直接在OnBnClickedButtonRun()函數(shù)中創(chuàng)建眾多線(xiàn)程,主線(xiàn)程將一直處于while循環(huán)中,而不能及時(shí)處理消息,所以就不能實(shí)時(shí)顯示創(chuàng)建的線(xiàn)程數(shù)目(不信的話(huà)大家可以試試)。
(3)注意線(xiàn)程函數(shù)參數(shù)的傳遞,和消息的發(fā)送。如果要傳遞多個(gè)參數(shù),可以使用結(jié)構(gòu)體傳遞。
實(shí)例說(shuō)明:該實(shí)例主要使用_beginthreadex()函數(shù),多人合作(5人)共同完成一項(xiàng)任務(wù),即平時(shí)大家說(shuō)的分工合作,齊頭并進(jìn)。
主要函數(shù)如下:
//.h文件中
//聲明線(xiàn)程處理函數(shù)
unsigned__stdcall ThreadFunc1(void*pArguments );//合作工作線(xiàn)程函數(shù)
unsigned__stdcall ThreadFunc2(void*pArguments );//檢測(cè)工作完成線(xiàn)程函數(shù)
//為了傳遞多個(gè)參數(shù),我采用結(jié)構(gòu)體
structthreadInfo
{
HWNDhWnd; //窗口句柄
int nOffset; //偏移量
int nWidth; //寬度
};
structthreadInfo2
{
HWND hWnd; //窗口句柄
HANDLE *phHandle; //線(xiàn)程句柄指針
};
// 實(shí)現(xiàn)
protected:
threadInfoInfo[5];
HANDLEm_hThead[5]; //用于存儲(chǔ)線(xiàn)程句柄
HANDLEhThead; //用于存儲(chǔ)線(xiàn)程句柄
unsigned m_dwThreadID[5];//用于存儲(chǔ)線(xiàn)程的ID
threadInfo2Info2;
//在.cpp文件中
//開(kāi)始按鈕函數(shù)
voidC_beginthreadexDlg::OnBnClickedButtonRun()
{
// TODO:在此添加控件通知處理程序代碼
//使能開(kāi)始按鈕:無(wú)效
GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);
CDC *dc =GetDC();
CRectrt;
GetClientRect(rt);
dc->FillSolidRect(0,0,rt.Width(),200,RGB(240,240,240));//刷新背景
ReleaseDC(dc);
intnWidth = rt.Width()/5;
//初始化線(xiàn)程的參數(shù)
Info[0].hWnd =Info[1].hWnd =Info[2].hWnd =Info[3].hWnd =Info[4].hWnd =GetSafeHwnd();
Info[0].nOffset = 0;Info[1].nOffset =nWidth;Info[2].nOffset= 2*nWidth;Info[3].nOffset = 3*nWidth;Info[4].nOffset= 4*nWidth;
Info[0].nWidth =Info[1].nWidth =Info[2].nWidth =Info[3].nWidth =Info[4].nWidth =nWidth;
//創(chuàng)建個(gè)模擬線(xiàn)程
for (inti = 0;i<5;i++)
{
m_hThead[i] = (HANDLE)_beginthreadex(NULL,0,ThreadFunc1,&Info[i],CREATE_SUSPENDED,&m_dwThreadID[i]);
}
GetDlgItem(IDC_STATIC1)->SetWindowText("進(jìn)行中...");
//開(kāi)始運(yùn)行
for (inti = 0;i<5;i++)
{
ResumeThread(m_hThead[i]);
}
//開(kāi)始運(yùn)行監(jiān)測(cè)結(jié)果線(xiàn)程
Info2.hWnd =m_hWnd;
Info2.phHandle =m_hThead;
hThead =(HANDLE)_beginthreadex(NULL,0,ThreadFunc2,&Info2,0,NULL);
}
//合作工作線(xiàn)程函數(shù)
unsigned__stdcall ThreadFunc1(void*pArguments )
{
threadInfo*info = (threadInfo*)pArguments;
CDC *dc =CWnd::FromHandle(info->hWnd)->GetDC();
for (inti=info->nOffset;i<info->nOffset+info->nWidth;i++)
{
for (intj =0 ;j<200;j++)
{
dc->SetPixel(i,j,RGB(0,0,0));
}
}
DeleteObject(dc);
return 0;
}
//等待所有的工作結(jié)束
unsigned__stdcall ThreadFunc2(void*pArguments )
{
threadInfo2*info = (threadInfo2*)pArguments;
//等待個(gè)線(xiàn)程中的一個(gè)完成
DWORDdwRet = WaitForMultipleObjects(5,info->phHandle,TRUE,INFINITE);
if (dwRet ==WAIT_FAILED)
{//出錯(cuò)啦
::SendMessage(info->hWnd,WM_GAMEOVER,1,0);
return 0;
}
//完成結(jié)束
::SendMessage(info->hWnd,WM_GAMEOVER,0,0);
return 0;
}
//消息處理函數(shù)
LRESULTC_beginthreadexDlg::OnGameOver(WPARAMwParam,LPARAMlParam)
{
if (wParam ==1)
{//出錯(cuò)
GetDlgItem(IDC_STATIC1)->SetWindowText("出錯(cuò)啦!");
}
else
{//成功
//顯示結(jié)果
GetDlgItem(IDC_STATIC1)->SetWindowText("任務(wù)完成!");
//閃動(dòng)顯示
CDC *dc = GetDC();
CRect rt;
GetClientRect(rt);
for (inti=0;i<8;i++)
{
dc->Draw3dRect(0,0,rt.Width(),200,RGB(240,240,240),RGB(240,240,240));//刷新背景
dc->Draw3dRect(1,1,rt.Width()-1,199,RGB(240,240,240),RGB(240,240,240));//刷新背景
Sleep(100);
dc->Draw3dRect(0,0,rt.Width(),200,RGB(255,0,0),RGB(255,0,0));//刷新背景
dc->Draw3dRect(1,1,rt.Width()-1,199,RGB(255,0,0),RGB(255,0,0));//刷新背景
Sleep(100);
}
ReleaseDC(dc);
}
//關(guān)閉句柄
for (inti=0;i<5;i++)
{
CloseHandle(m_hThead[i]);
}
CloseHandle(hThead);
//使能開(kāi)始按鈕,以便可以開(kāi)始下一次比賽
GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(TRUE);
return 0;
}
運(yùn)行效果:
工程源碼下載地址:
http://download.csdn.net/detail/cbnotes/4905693
歡迎大家修改和指正。
注意事項(xiàng):
(1)由于_beginthreadex()創(chuàng)建的線(xiàn)程結(jié)束后自動(dòng)調(diào)用_endthreadex()函數(shù),但是不會(huì)關(guān)閉線(xiàn)程句柄,所以必須手動(dòng)關(guān)閉句柄,但能使用同步函數(shù),如:
WaitForSingleObject()/WaitForMultipleObjects()函數(shù)來(lái)同步。
(2)通過(guò)該實(shí)例還可以稍加改進(jìn),就可以統(tǒng)計(jì)5個(gè)線(xiàn)程的各自的工作時(shí)間,并排序。歡迎大家多多練習(xí)。
(3)由_beginthreadex()函數(shù)創(chuàng)建線(xiàn)程,能靈和控制和管理線(xiàn)程,所以推薦使用該方式。
=================================================轉(zhuǎn)載請(qǐng)標(biāo)明出處,謝謝.http://blog.csdn.net/cbNotes
聯(lián)系客服