詳解_beginthreadex()創建線程

一、使用_beginthreadex()             

需要的頭文件支持#include         // for _beginthread()
需要的設置:ProjectàSetting-->C/C++-->User run-time library 選擇Debug Multithreaded 或者Multithreaded。即使用: MTMTD

代碼如下:     
 

#include <stdio.h>
#include <string>             // for STL string class
#include <windows.h>          // for HANDLE
#include <process.h>          // for _beginthread()
using namespace std;

class ThreadX
{
private:
  int loopStart;
  int loopEnd;
  int dispFrequency;
public:
  string threadName;

  ThreadX( int startValue, int endValue, int frequency )
  {
    loopStart = startValue;
    loopEnd = endValue;
    dispFrequency = frequency;
  }

  static unsigned __stdcall ThreadStaticEntryPoint(void * pThis)
  {
      ThreadX * pthX = (ThreadX*)pThis;   // the tricky cast
      pthX->ThreadEntryPoint();           // now call the true entry-point-function
      return 1;                              // the thread exit code
  }

  void ThreadEntryPoint()
  {
    for (int i = loopStart; i <= loopEnd; ++i)
    {
      if (i % dispFrequency == 0)
      {
          printf( "%s: i = %d\n", threadName.c_str(), i );
      }
    }
    printf( "%s thread terminating\n", threadName.c_str() );
  }
};


int main()
{
    ThreadX * o1 = new ThreadX( 0, 1, 2000 );

    HANDLE   hth1;
    unsigned  uiThread1ID;

    hth1 = (HANDLE)_beginthreadex( NULL,         // security
                                   0,            // stack size
                                   ThreadX::ThreadStaticEntryPoint,
                                   o1,           // arg list
                                   CREATE_SUSPENDED,  // so we can later call ResumeThread()
                                   &uiThread1ID );

    if ( hth1 == 0 )
        printf("Failed to create thread 1\n");

    DWORD   dwExitCode;
    GetExitCodeThread( hth1, &dwExitCode );  // should be STILL_ACTIVE = 0x00000103 = 259
    printf( "initial thread 1 exit code = %u\n", dwExitCode );

    o1->threadName = "t1";

    ThreadX * o2 = new ThreadX( -100000, 0, 2000 );

    HANDLE   hth2;
    unsigned  uiThread2ID;

    hth2 = (HANDLE)_beginthreadex( NULL,         // security
                                   0,            // stack size
                                   ThreadX::ThreadStaticEntryPoint,
                                   o2,           // arg list
                                   CREATE_SUSPENDED,  // so we can later call ResumeThread()
                                   &uiThread2ID );

    if ( hth2 == 0 )
        printf("Failed to create thread 2\n");

    GetExitCodeThread( hth2, &dwExitCode );  // should be STILL_ACTIVE = 0x00000103 = 259
    printf( "initial thread 2 exit code = %u\n", dwExitCode );

    o2->threadName = "t2";

    ResumeThread( hth1 );   // serves the purpose of Jaeschke's t1->Start()
    ResumeThread( hth2 );   

    WaitForSingleObject( hth1, INFINITE );
    WaitForSingleObject( hth2, INFINITE );

    GetExitCodeThread( hth1, &dwExitCode );
    printf( "thread 1 exited with code %u\n", dwExitCode );

    GetExitCodeThread( hth2, &dwExitCode );
    printf( "thread 2 exited with code %u\n", dwExitCode );

    CloseHandle( hth1 );
    CloseHandle( hth2 );

    delete o1;
    o1 = NULL;

    delete o2;
    o2 = NULL;

    printf("Primary thread terminating.\n");
    return 0;
}

註意:

  • (1)如果你正在編寫C/C++代碼,決不應該調用CreateThread。相反,應該使用VisualC++運行期庫函數_beginthreadex,退出也應該使用_endthreadex。如果不使用Microsoft的VisualC++編譯器,你的編譯器供應商有它自己的CreateThread替代函數。不管這個替代函數是什麼,你都必須使用。
  • (2)因為_beginthreadex_endthreadex是CRT線程函數,所以必須註意編譯選項runtimelibaray的選擇,使用MT或MTD。[MultiThreaded , Debug MultiThreaded]
  • (3)_beginthreadex函數的參數列表與CreateThread函數的參數列表是相同的,但是參數名和類型並不完全相同。這是因為Microsoft的C/C++運行期庫的開發小組認為,C/C++運行期函數不應該對Windows數據類型有任何依賴。_beginthreadex函數也像CreateThread那樣,返回新創建的線程的句柄。
  • (4)C++主線程的終止,同時也會終止所有主線程創建的子線程,不管子線程有沒有執行完畢。所以上面的代碼中如果不調用WaitForSingleObject,則2個子線程t1和t2可能並沒有執行完畢或根本沒有執行。
  • (5)如果某線程掛起,然後有調用WaitForSingleObject等待該線程,就會導致死鎖。所以上面的代碼如果不調用resumethread,則會死鎖。

二、_beginthreadex()與代CreateThread()區別                                   

CreateThread是Windows的API函數(SDK函數的標準形式,直截瞭當的創建方式,任何場合都可以使用),提供操作系統級別的創建線程的操作,且僅限於工作者線程。不調用MFC和RTL的函數時,可以用CreateThread,其它情況不要輕易。在使用的過程中要考慮到進程的同步與互斥的關系(防止死鎖)。線程函數定義為:DWORD WINAPI _yourThreadFun(LPVOID pParameter)。但它沒有考慮:

  • (1)C Runtime中需要對多線程進行紀錄和初始化,以保證C函數庫工作正常(典型的例子是strtok函數)。
  • (2)MFC也需要知道新線程的創建,也需要做一些初始化工作(當然,如果沒用MFC就沒事瞭)。  

AfxBeginThread:MFC中線程創建的MFC函數,首先創建瞭相應的CWinThread對象,然後調用CWinThread::CreateThread,   在CWinThread::CreateThread中,完成瞭對線程對象的初始化工作,然後,調用_beginthreadex(AfxBeginThread相比較更為安全)創建線程。它簡化瞭操作或讓線程能夠響應消息,即可用於界面線程,也可以用於工作者線程,但要註意不要在一個MFC程序中使用_beginthreadex()或CreateThread()。線程函數定義為:UINT
_yourThreadFun(LPVOID pParam)

_beginthreadex:MS對C Runtime庫的擴展SDK函數,首先針對C Runtime庫做瞭一些初始化的工作,以保證C Runtime庫工作正常。然後,調用CreateThread真正創建線程。 僅使用Runtime Library時,可以用_BegingThread。

  • 小節:實際上,這三個函數之間存在一定的調用關系,第一個純粹一些,後兩個完成自己相應的工作之後,調用前者實現線程的創建。其中CreateThread是由操作系統提供的接口,而AfxBeginThread_BeginThread則是編譯器對它的封裝。
  • 小節:用_beginthreadex()_endthreadex函數應該是最佳選擇,且都是C Run-time Library中的函數,函數的參數和數據類型都是C Run-time Library中的類型,這樣在啟動線程時就不需要進行Windows數據類型和C Run-time Library中的數據類型之間的轉化,從而,減低瞭線程啟動時的資源消耗和時間的消耗。但使用_beginthread,無法創建帶有安全屬性的新線程,無法創建暫停的線程,也無法獲得 線程ID,_endthread的情況類似,它不帶參數,這意味這線程的退出代碼必須硬編碼為0。
  • 小節:MFC也是C++類庫(隻不過是Microsoft的C++類庫,不是標準的C++類庫),在MFC中也封裝瞭new和delete兩中運算符,所以用到new和delete的地方不一定非要使用_beginthreadex() 函數,用其他兩個函數都可以。

_beginthreadex_beginthread在回調入口函數之前進行一些線程相關的CRT的初始化操作。
CRT的函數庫在線程出現之前就已經存在,所以原有的CRT不能真正支持線程,
這也導致瞭許多CRT的函數在多線程的情況下必須有特殊的支持,不能簡單的使用CreateThread就OK。
 

補充一點:_beginthreadex()是針對CRT的線程函數,在線程中若要用到CRT的函數,最好用這個啟動線程,如果不用這個會有內存泄漏。

到此這篇關於詳解_beginthreadex()創建線程的文章就介紹到這瞭,更多相關_beginthreadex()創建線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: