1. 程式人生 > >C++多執行緒函式_beginthread/_beginthreadex/CreateThread

C++多執行緒函式_beginthread/_beginthreadex/CreateThread

Part1:

1、CRT簡介:

CRT: (C Runtime Library)即C執行時庫,是系統執行的基礎,包含了c常用的函式集(如:printf,malloc,strcpy等),為執行main做了初始化環境變數、堆、io等資源,並在結束後清理。

在Windows環境下,VC提供的 C run-time library又分為動態執行時庫、靜態執行時庫、多執行緒、單執行緒、除錯版本(Debug)、發行版本(Release)等。

開關

對應的庫

版本

/MD

MSVCRT.LIB

多執行緒DLLRelease

版本

/MDd

MSCVRTD.LIB

多執行緒DLLDebug版本

/MT

LIBCMT.LIB

多執行緒靜態連結的Release版本

/MTd

LIBCMTD.LIB

多執行緒靜態連結的Debug版本

/clr

MSVCMRT.LIB

託管程式碼和非託管程式碼混合

/clr:pure

MSVCURT.LIB

純託管程式碼

圖1:編譯器執行庫設定(VS2008)

具體請參考MSDN:

ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs/dv_vccrt/html/a889fd39-807d-48f2-807f-81492612463f.htm

2、使用CRT的多執行緒函式集:兩組

序號

函式名

功能

1

_beginthread()

建立一個新執行緒

2

_endthread()

結束一個執行緒的執行

3

_beginthreadex()

建立一個新執行緒

4

_endthreadex()

結束一個執行緒的執行

5

ResumeThread()

恢復執行緒的執行

6

SuspendThread()

掛起執行緒

7

GetExiCodeThread()

得到一個執行緒的退出碼

8

WaitForSingleObject()

等待單個物件

9

WaitForMultipleObjects()

等待多個物件

3、兩組函式的說明:

3.1、_beginthread和_endthread
該函式是C Runtime Library中的函式。其原型如下

unsigned long _beginthread(

void( __cdecl *start_address )( void * ),//執行緒函式的起始地址

unsigned stack_size,//堆疊大小,設定0為系統預設值

void *arglist );//傳遞給執行緒函式的引數,沒有則為NULL

“該函式被認為是頭腦簡單的函式”,使用該函式導致無法有效的控制被建立執行緒,如不能在啟動時將該執行緒掛起,無法為該執行緒設定優先權等。另外,無法利用這個Handle來等待該執行緒結束等操作。該函式是早期的C Runtime Library的產物,不提倡使用,後期的改良版本為_beginthreadex。
通過_beginthread啟動的執行緒在應當通過呼叫_endthread結束,以保證清除與執行緒相關的資源。_endthread的原型為:

void _endthread(void);

3.2、_beginthreadex和_endthreadex
該函式是C Runtime Library中的一個函式,用標準C實現,相比_beginthread,_beginthreadex對執行緒控制更為有力(比前者多三個引數),是_beginthread的加強版。其原型為:

unsignedlong _beginthreadex(

void *security,//執行緒函式的安全描述符

unsigned stack_size,// 堆疊大小,設定0為系統預設值

unsigned ( __stdcall *start_address )( void * ),//執行緒函式的起始地址

void*arglist, //傳遞給執行緒函式的引數,沒有則為NULL

unsignedinitflag,//初始狀態,0為立即執行,CREATE_SUSPEND為建立後掛起

unsigned*thrdaddr );//指向一個32為的變數,存放執行緒識別符號

該函式返回新執行緒的控制代碼,通過該控制代碼可實現對執行緒的控制。雖然,該函式是用標準C寫的(即可不加修改就可以移植到其他系統執行),但是由於它與Windows系統有著緊密的聯絡(需要手動關閉該執行緒產生的Handle),因此實現時,往往需要包含windows.h。
通過_beginthreadex啟動的執行緒通過呼叫_endthreadex做相關清理。該函式比較像CreateThread函式。

_endthreadex函式的原型為:

void _endthreadex(unsigned retVal);

關於這兩組函式的詳細區別請參考MSDN的說明:

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

);

Parameters

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.

Return Value

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 in Parameter 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.

Remarks

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.

Requirements

Routine

Required header

_beginthread

<process.h>

_beginthreadex

<process.h>

For more compatibility information, see Compatibility in theIntroduction.

Libraries

Multithreaded versions of the C run-time libraries only.

3.3、執行緒函式的定義:

_beginthread()和_beginthreadex()的執行緒執行函式的定義是不一樣的。

對於_beginthread()建立的執行緒,其執行緒函式定義為:

void ThreadPro(void * pArguments );

對於_beginthreadex()建立的執行緒,其執行緒函式定義為:

unsigned __stdcallThreadFunc( void* pArguments )

4、注意事項:

(1)兩者建立執行緒函式方式不同,_beginthreadex()的執行緒函式必須使用__stdcall呼叫方式,而且必須返回一個unsigned型的退出碼。

(2)_beginthreadex()在建立執行緒失敗時返回0,而_beginthread()在建立執行緒失敗時返回-1,這一點在檢測返回結果時必須注意。

(3)如果是呼叫_beginthread()建立執行緒,並相應地呼叫_endthread()結束執行緒時,系統將自動關閉執行緒控制代碼。而呼叫_beginthreadex()建立執行緒,並相應地呼叫_endthreadex()結束執行緒時,系統不能自動關閉執行緒控制代碼。

(4)由於_beginthread()建立執行緒引數比較簡單,不能控制執行緒的初始啟動狀態,且不返回建立的執行緒控制代碼,也不能呼叫

WaitForSingleObject()/WaitForMultipleObjects()函式。所以一般不常用,而_beginthreadex()與CreatThread()函式比較相似。能方便控制執行緒。

5、多執行緒例項:

5.1、例項1:

例項說明:該例項主要使用_beginthread()函式,建立儘可能多的執行緒,知道系統不能再建立為止。注意實時資料的顯示和引數的傳遞,如果想傳遞多個引數,則可以使用結構體。

主要函式如下:

//在.h檔案中:

#define WM_ADD WM_USER+10//定義訊息

void ThreadFunc1(void *pArg);//執行緒函式1:開始建立

void ThreadFunc2(void *pArg);//執行緒函式2

LRESULT OnMyMsg(WPARAMLPARAM);//訊息函式  

//在.cpp檔案中:

//全域性變數,一般在cpp中定義,不要在.h定義,否則編譯出錯:變數重複定義

bool g_bRun = false;

long g_nCount = 0;

//開始按鈕函式

void C_beginthreadDlg::OnBnClickedButtonRun()

{

    // TODO: 在此新增控制元件通知處理程式程式碼

    //建立執行緒:主要是用來建立執行建立執行緒池的執行緒

    if (_beginthread(ThreadFunc1,0,&m_hWnd) != -1 )

    {//成功

       GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);

    }

}

//建立執行緒池的執行緒函式

void ThreadFunc1(void *pArg)

{

    HWND *hWnd = (HWND*)pArg;//得到視窗控制代碼

    g_nCount= 0;//初始化變數

    g_bRun = true;

    while (g_bRun)

    {//開始建立儘可能多的執行緒

       if (_beginthread(ThreadFunc2,0,hWnd) == -1)

       {//失敗則結束

           g_bRunfalse;

           break;

       }

    }

    //退出

    ::SendMessage(*hWnd,WM_ADD,1,0); 

}

//執行緒函式

void ThreadFunc2(void *pArg)

{

    HWND *hWnd = (HWND*)pArg;

    g_nCount++;

    ::SendMessage(*hWnd,WM_ADD,0,g_nCount);//顯示個數

    while(g_bRun)

    {

       Sleep(1000);

    }

}

// WM_ADD訊息處理函式

LRESULT C_beginthreadDlg::OnMyMsg(WPARAM wParamLPARAM lParam)

{

    if (wParam == 1)

    {//結束

       GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(TRUE);

    }

    else

    {//顯示更新

       m_nCountg_nCount;

        UpdateData(FALSE);

    }

    return 0;

}

執行效果:

圖2:例項1演示

工程原始碼下載地址:

歡迎大家修改和指正。

注意事項:

(1)由於_beginthread()建立的執行緒結束後自動關閉執行緒控制代碼,所以不能使用WaitForSingleObject()/WaitForMultipleObjects()函式來同步。

(2)上面的程式中,先是建立了執行緒1(ThreadFunc1),再用該執行緒1來建立眾多執行緒,而不是直接在OnBnClickedButtonRun()函式中建立眾多執行緒,大家知道這是為什麼嗎?主要是為了能夠實時顯示建立的執行緒數目。如果直接在OnBnClickedButtonRun()函式中建立眾多執行緒,主執行緒將一直處於while迴圈中,而不能及時處理訊息,所以就不能實時顯示建立的執行緒數目(不信的話大家可以試試)。

(3)注意執行緒函式引數的傳遞,和訊息的傳送。如果要傳遞多個引數,可以使用結構體傳遞。

5.2、例項2:

例項說明:該例項主要使用_beginthreadex()函式,多人合作(5人)共同完成一項任務,即平時大家說的分工合作,齊頭並進。

主要函式如下:

//.h檔案中

//宣告執行緒處理函式

unsigned __stdcall ThreadFunc1void*pArguments );//合作工作執行緒函式

unsigned __stdcall ThreadFunc2void*pArguments );//檢測工作完成執行緒函式

//為了傳遞多個引數,我採用結構體

struct threadInfo

{

    HWND hWnd;       //視窗控制代碼

    int  nOffset;    //偏移量

    int  nWidth;     //寬度

};

struct threadInfo2

{

    HWND   hWnd;           //視窗控制代碼

    HANDLE *phHandle;     //執行緒控制代碼指標

};

// 實現

protected:

    threadInfoInfo[5];

    HANDLE m_hThead[5];    //用於儲存執行緒控制代碼

    HANDLE hThead;     //用於儲存執行緒控制代碼

    unsigned  m_dwThreadID[5];//用於儲存執行緒的ID

   threadInfo2Info2;

//在.cpp檔案中

//開始按鈕函式

void C_beginthreadexDlg::OnBnClickedButtonRun()

{

    // TODO: 在此新增控制元件通知處理程式程式碼

    //使能開始按鈕:無效

    GetDlgItem(IDC_BUTTON_RUN)->EnableWindow(FALSE);

    CDC *dc = GetDC();

    CRect rt;

    GetClientRect(rt);

    dc->FillSolidRect(0,0,rt.Width(),200,RGB(240,240,240));//重新整理背景

    ReleaseDC(dc);

    int nWidth = rt.Width()/5;

    //初始化執行緒的引數

    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;

    //建立個模擬執行緒

    for (int i = 0;i<5;i++)

    {  

       m_hThead[i] = (HANDLE)_beginthreadex(NULL,0,ThreadFunc1,&Info[i],CREATE_SUSPENDED,&m_dwThreadID[i]); 

    }

    GetDlgItem(IDC_STATIC1)->SetWindowText("進行中...");

    //開始執行

    for (int i = 0;i<5;i++)

    {  

       ResumeThread(m_hThead[i]); 

    }

    //開始執行監測結果執行緒

    Info2.hWnd = m_hWnd;

    Info2.phHandle = m_hThead;

    hThead =(HANDLE)_beginthreadex(NULL,0,ThreadFunc2,&Info2,0,NULL);

}

//合作工作執行緒函式

unsigned __stdcall ThreadFunc1void*pArguments )

{

    threadInfo*info = (threadInfo*)pArguments;

    CDC *dc = CWnd::FromHandle(info->hWnd)->GetDC();

    for (int i=info->nOffset;i<info->nOffset+info->nWidth;i++)

    {

       for (int j =0 ;j<200;j++)

       {  

           dc->SetPixel(i,j,RGB(0,0,0));

       }

    }

    DeleteObject(dc);

    return 0;

}

//等待所有的工作結束

unsigned __stdcall ThreadFunc2void*pArguments )

{

    threadInfo2*info = (threadInfo2*)pArguments;

    //等待個執行緒中的一個完成

    DWORD dwRet = WaitForMultipleObjects(5,info->phHandle,TRUE,INFINITE);

    if (dwRet == WAIT_FAILED)

    {//出錯啦

       ::SendMessage(info->hWnd,

相關推薦

C++執行函式_beginthread/_beginthreadex/CreateThread

Part1: 1、CRT簡介: CRT: (C Runtime Library)即C執行時庫,是系統執行的基礎,包含了c常用的函式集(如:printf,malloc,strcpy等),為執行main做了初始化環境變數、堆、io等資源,並在結束後清理。 在

C++執行函式CreateThread()詳解

採用CreateThread()建立多執行緒程式 原創 2012年12月10日 11:44:59

C++執行系列(一)CreateThread_beginthreadex區別

現在在學習多執行緒,順便將蒐集到的資料整理下來以供參考和查詢。首先在開始多執行緒學習的時候遇到的首要問題便是多執行緒的建立,在查閱資料後有CreateThread和_beginthreadex兩種方法,可能不止這兩種,以後學習到了再補充。-------------------

C++執行CreateThread

WIN32API,需要包含標頭檔案windows.h API簡介 簡單瞭解下,平時用到的也就那兩三個。 介面 簡介 SwitchToThread 切換到另一個可排程執行緒 CreateThread 建立執行緒 CreateRemoteTh

C/C++函式的本質以及執行函式的呼叫過程

C/C++中,函式的本質是一段可執行程式碼,程式碼包括了局部變數、全域性變數的地址等等。到組合語言的級別,變數函式等都可以視為彙編的程式碼片段。函式的本質就是一個可執行程式碼片段的集合 執行緒的詳細介紹:http://www.cnblogs.com/tracylee/archive/

C++執行例項(_beginThreadex建立執行)

C++多執行緒(二)(_beginThreadex建立多執行緒)   C/C++ Runtime 多執行緒函式 一 簡單例項(來自codeprojct:http://www.codeproject.com/useritems/MultithreadingTutorial.asp) 主執行緒建立2個執行緒t1

C執行程式設計以及執行函式

執行緒的資料處理   和程序相比,執行緒的最大優點之一是資料的共享性,各個程序共享父程序處沿襲的資料段,可以方便的獲得、修改資料。但這也給多執行緒程式設計帶來了許多問題。我們必須當心有多個不同的程序訪問相同的變數。許多函式是不可重入的,即同時不能執行一個函式的多個拷貝(除非使用不同的資料段)。在函式中宣告的

c# 執行執行一個函式

(1)感謝宣告 首先感謝黃師,劉老師給於的指導(學校的指導老師) 其次感謝這篇部落格的博主寫出了這麼好的文章:http://www.cnblogs.com/yunfeifei/p/3993401.ht

c/c++ 執行 std::lock

多執行緒 std::lock 當要同時操作2個物件時,就需要同時鎖定這2個物件,而不是先鎖定一個,然後再鎖定另一個。同時鎖定多個物件的方法:std::lock(物件1.鎖,物件2.鎖...) 額外說明:lock_guard<mutex> lock_a(d1.m, std::adopt_lock

C#執行順序依賴執行控制

在開發過程中,經常需要多個任務並行的執行的場景,同時任務之間又需要先後依賴的關係。針對這樣的處理邏輯,通常會採用多執行緒的程式模型來實現。   比如A、B、C三個執行緒,A和B需要同時啟動,並行處理,且B需要依賴A完成,在進行後續的處理,C需要B完成後開始處理。  

C/C++ 執行機制

一、C/C++多執行緒操作說明 C/C++多執行緒基本操作如下: 1. 執行緒的建立結束 2. 執行緒的互斥和同步 3. 使用訊號量控制執行緒 4. 執行緒的基本屬性配置  在C/C++程式碼編寫時,使用多執行緒機制,首先需要做的事情就是宣告引用,具體如下

c/c++ 執行 std::once

多執行緒 std::once 轉自:https://blog.csdn.net/hengyunabc/article/details/33031465 std::once的特點:即使有多個執行緒要訪問同一個函式,只有一個執行緒會成功。 std::once的用途:當某個資料只有在初始化的時候需要執行緒安全

C#執行基礎(執行的優先順序、狀態、同步)

一、關於多執行緒的優先順序、狀態、同步指令碼如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System

C++執行之std::thread

C++11,包含標頭檔案 thread.h,並使用名稱空間std。 thread類提供的方法 方法 描述 thread 建構函式,在這裡傳入執行緒執行函式,和函式引數

C++ 執行pthread 學習筆記

本篇是我在學習C++多執行緒的時候做的筆記,主要記錄的是基礎的流程,部分程式碼例項,以及重點函式的說明。 pthread 入口函式型別說明 void * func1(void * t) void* 表示無型別指標 void*作為函式引數,表示函式接收一個指標,不管是什麼型別

2017.10.21 C# 執行控制控制元件例項

直接上程式碼片段 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text

2017.10.12 C#執行與非同步的區別

最近在寫個多執行緒處理的程式,又重新溫習了一下相關知識,記錄在這裡。 C#多執行緒與非同步的區別 原文地址:http://kb.cnblogs.com/page/116095/ 多執行緒和非同步操作的異同   多執行緒和非同步操作兩者都可以達到避免呼叫執行緒阻塞的目的,從而提高軟體

linux c 執行開發

在開發多執行緒程式時,當建立的執行緒數量特別多的時候,就會遇到執行緒數量的瓶頸。 多執行緒設定 設定核心引數 kernel.threads-max kernel.threads-max 是 linux 系統允許建立的最大執行緒數,預設是 7767 修改 /etc/sysc

C++:蟻群演算法解決TSP(C++執行版)

TSP問題:旅行商問題,最短迴路。 這裡採用att48資料,鄰接矩陣全部取整數,原資料放在文後。 解決程式碼如下: //#define TEST_INPUT //#define TEST_T //#define TEST_ANT //#define TEST_VALUE #

c/c++ 執行 ubuntu18.04 boost編譯與執行的坑

多執行緒 boost編譯與執行的坑 背景:因為要使用boost裡的多執行緒庫,所以遇到了下面的坑。 系統版本:ubuntu18.04 一,安裝boost 1,去boost官網下載 boost_1_XX_0.tar.gz 2,解壓 tar -zxvf boost_1_65_0.tar.gz 3