_beginthread, _beginthreadex

建立執行緒。

語法

uintptr_t _beginthread( // NATIVE CODE
   void( __cdecl *start_address )( void * ),
   unsigned stack_size,
   void *arglist
);
uintptr_t _beginthread( // MANAGED CODE
   void( __clrcall *start_address )( void * ),
   unsigned stack_size,
   void *arglist
);
uintptr_t _beginthreadex( // NATIVE CODE
   void *security,
   unsigned stack_size,
   unsigned ( __stdcall *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr
);
uintptr_t _beginthreadex( // MANAGED CODE
   void *security,
   unsigned stack_size,
   unsigned ( __clrcall *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr
);

參數

start_address
開始執行新執行緒之常式的起始位址。 針對 ,呼叫慣例為 __cdecl (適用于 _beginthread 機器碼)或 __clrcall (針對 Managed 程式碼)。 針對 ,呼叫慣例為 __stdcall (適用于 _beginthreadex 機器碼)或 __clrcall (針對 Managed 程式碼)。

stack_size
新執行緒堆疊大小或 0。

arglist
要傳遞至新執行緒的引數清單,或 NULL

Security
SECURITY_ATTRIBUTES結構的指標,判斷傳回的控制碼是否可以由子進程繼承。 如果 SecurityNULL ,則無法繼承控制碼。

initflag
控制新執行緒之初始狀態的旗標。 將 設定 initflag 為 0 以立即執行,或設定 CREATE_SUSPENDED 為 以暫止狀態建立執行緒;使用 ResumeThread 來執行執行緒。 設定 initflagSTACK_SIZE_PARAM_IS_A_RESERVATION 旗標,以位元組為單位作為 stack_size 堆疊的初始保留大小;如果未指定此旗標, stack_size 則指定認可大小。

thrdaddr
指向接收執行緒識別項的 32 位元變數。 NULL如果是 ,則不會使用。

傳回值

如果成功,則上述每個函式都會將控制代碼傳回至新建立的執行緒;然而,如果新建立的執行緒太快結束, _beginthread 可能無法傳回有效的控制代碼 (請參閱一節中的討論。發生錯誤時, _beginthread 會傳回 -1L,如果 errno 執行緒太多, EINVAL 則設定為 ;如果引數無效或堆疊大小不正確,或資源不足(例如記憶體)則設定 EAGAINEACCES 。 發生錯誤時, _beginthreadex 會傳回 0,並設定 errno_doserrno

如果 start_addressNULL ,則會叫用不正確參數處理常式,如參數驗證 中所述 。 如果允許繼續執行,這些函式會將 errno 設定為 EINVAL ,並傳回 -1。

如需這些傳回碼和其他傳回碼的詳細資訊,請參閱 errno_sys_errlist_doserrno_sys_nerr

如需 的詳細資訊 uintptr_t ,請參閱 標準類型

備註

_beginthread 函式會建立一個從 start_address開始執行常式的執行緒。 在 start_address 的常式必須使用 __cdecl (若為機器碼) 或 __clrcall (若為 Managed 程式碼) 呼叫慣例,而且不應該有傳回值。 當執行緒從該常式傳回時,它會自動終止。 如需執行緒的詳細資訊,請參閱 舊版程式碼的多執行緒支援(Visual C++)。

_beginthreadex 與 Win32 CreateThread API 更類似 _beginthread_beginthreadex_beginthread 在下列各方面不同:

  • _beginthreadex 還有三個參數: initflagSecuritythreadaddr 。 新執行緒可以在指定安全性的暫停狀態下建立,並且可以使用執行緒識別項 thrdaddr來加以存取。

  • 位於傳遞至 start_address_beginthreadex 的常式必須使用 __stdcall (若為機器碼) 或 __clrcall (若為 Managed 程式碼) 呼叫慣例,而且必須傳回執行緒結束代碼。

  • _beginthreadex 會在失敗時回傳 0,而非 -1L。

  • 使用 _beginthreadex 所建立的執行緒會由 呼叫 _endthreadex 終止。

比起 _beginthreadex_beginthread 函式給予您更多掌控執行緒建立方式的能力。 _endthreadex 函式也更有彈性。 例如,您可以透過 _beginthreadex,使用安全性資訊、設定執行緒初始狀態 (執行或暫停),以及取得新建立執行緒的執行緒識別項。 您也可以搭配同步處理 API 使用 所 _beginthreadex 傳回的執行緒控制碼,您無法使用 _beginthread

_beginthreadex 比 使用 _beginthread 更安全。 如果 _beginthread 產生的執行緒結束得過快,傳回給 _beginthread 呼叫端的控制代碼可能會無效,或指向其他執行緒。 不過,所傳回 _beginthreadex 的控制碼必須由 的呼叫端 _beginthreadex 關閉,因此如果 _beginthreadex 未傳回錯誤,它保證為有效的控制碼。

您可以 _endthread 呼叫 或 _endthreadex 明確終止執行緒;不過, _endthread_endthreadex 當執行緒從傳遞為參數的常式傳回時,或會自動呼叫 。 透過呼叫 _endthread_endthreadex 終止執行緒有助於確保正確復原配置給執行緒的資源。

_endthread 會自動關閉執行緒控制碼,但 _endthreadex 不會。 因此,當您使用 _beginthread_endthread 時,請勿藉由呼叫 Win32 CloseHandle API 明確關閉執行緒控制碼。 此行為與 Win32 ExitThread API 不同。

注意

對於與 Libcmt.lib 連結的可執行檔,請勿呼叫 Win32 ExitThread 應用程式開發介面,這樣才不會阻止執行階段系統回收配置的資源。 _endthread_endthreadex 會回收配置的執行緒資源,然後呼叫 ExitThread

呼叫 _beginthread_beginthreadex 時,作業系統會處理堆疊的配置,您不必將執行緒堆疊的位址傳遞給這兩個函式。 此外, stack_size 引數也可以是 0,此時作業系統會使用與指定給主執行緒的堆疊相同的值。

arglist 是要傳遞給新建立執行緒的參數。 一般而言,它是資料項目的位址,例如字元字串。 arglist 如果 NULL 不需要,但 _beginthread_beginthreadex 必須提供一些值,才能傳遞至新的執行緒。 如果有任何執行緒呼叫 abortexit_exitExitProcess,就會結束所有的執行緒。

新執行緒的地區設定是使用每個進程全域目前地區設定資訊初始化。 如果呼叫 _configthreadlocale 啟用個別執行緒地區設定(僅限全域或僅限新執行緒),執行緒可以藉由呼叫 setlocale_wsetlocale ,從其他執行緒獨立變更其地區設定。 未設定個別執行緒地區設定旗標的執行緒可能會影響所有其他執行緒中也未設定個別執行緒地區設定旗標的地區設定資訊,以及所有新建立的執行緒。 如需詳細資訊,請參閱 Locale

針對 /clr 程式碼, _beginthread_beginthreadex 各有兩個多載。 其中一個採用原生呼叫慣例函式指標,另一個則採用函 __clrcall 式指標。 第一個多載不是應用程式域安全且永遠不會。 如果您要撰寫 /clr 程式碼,您必須確定新執行緒在存取受控資源之前進入正確的應用程式域。 例如, call_in_appdomain 您可以使用 來執行此動作。 第二個多載版本是應用程式定義域安全的;新建立的執行緒永遠都會在 _beginthread_beginthreadex呼叫端的應用程式定義域中結束。

根據預設,此函式的全域狀態會限定于應用程式。 若要變更此行為,請參閱 CRT 中的全域狀態。

需求

常式 必要的標頭
_beginthread <process.h>
_beginthreadex <process.h>

如需相容性詳細資訊,請參閱相容性

程式庫

僅限 C 執行階段程式庫 的多執行緒版本。

若要使用 _beginthread_beginthreadex,應用程式必須連結其中一個多執行緒 C 執行階段程式庫。

範例

下列範例會使用 _beginthread_endthread

// crt_BEGTHRD.C
// compile with: /MT /D "_X86_" /c
// processor: x86
#include <windows.h>
#include <process.h>    /* _beginthread, _endthread */
#include <stddef.h>
#include <stdlib.h>
#include <conio.h>

void Bounce( void * );
void CheckKey( void * );

// GetRandom returns a random integer between min and max.
#define GetRandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))
// GetGlyph returns a printable ASCII character value
#define GetGlyph( val ) ((char)((val + 32) % 93 + 33))

BOOL repeat = TRUE;                 // Global repeat flag
HANDLE hStdOut;                     // Handle for console window
CONSOLE_SCREEN_BUFFER_INFO csbi;    // Console information structure

int main()
{
    int param = 0;
    int * pparam = &param;

    // Get display screen's text row and column information.
    hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
    GetConsoleScreenBufferInfo( hStdOut, &csbi );

    // Launch CheckKey thread to check for terminating keystroke.
    _beginthread( CheckKey, 0, NULL );

    // Loop until CheckKey terminates program or 1000 threads created.
    while( repeat && param < 1000 )
    {
        // launch another character thread.
        _beginthread( Bounce, 0, (void *) pparam );

        // increment the thread parameter
        param++;

        // Wait one second between loops.
        Sleep( 1000L );
    }
}

// CheckKey - Thread to wait for a keystroke, then clear repeat flag.
void CheckKey( void * ignored )
{
    _getch();
    repeat = 0;    // _endthread implied
}

// Bounce - Thread to create and control a colored letter that moves
// around on the screen.
//
// Params: parg - the value to create the character from
void Bounce( void * parg )
{
    char       blankcell = 0x20;
    CHAR_INFO  ci;
    COORD      oldcoord, cellsize, origin;
    DWORD      result;
    SMALL_RECT region;

    cellsize.X = cellsize.Y = 1;
    origin.X = origin.Y = 0;

    // Generate location, letter and color attribute from thread argument.
    srand( _threadid );
    oldcoord.X = region.Left = region.Right =
        GetRandom(csbi.srWindow.Left, csbi.srWindow.Right - 1);
    oldcoord.Y = region.Top = region.Bottom =
        GetRandom(csbi.srWindow.Top, csbi.srWindow.Bottom - 1);
    ci.Char.AsciiChar = GetGlyph(*((int *)parg));
    ci.Attributes = GetRandom(1, 15);

    while (repeat)
    {
        // Pause between loops.
        Sleep( 100L );

        // Blank out our old position on the screen, and draw new letter.
        WriteConsoleOutputCharacterA(hStdOut, &blankcell, 1, oldcoord, &result);
        WriteConsoleOutputA(hStdOut, &ci, cellsize, origin, &region);

        // Increment the coordinate for next placement of the block.
        oldcoord.X = region.Left;
        oldcoord.Y = region.Top;
        region.Left = region.Right += GetRandom(-1, 1);
        region.Top = region.Bottom += GetRandom(-1, 1);

        // Correct placement (and beep) if about to go off the screen.
        if (region.Left < csbi.srWindow.Left)
            region.Left = region.Right = csbi.srWindow.Left + 1;
        else if (region.Right >= csbi.srWindow.Right)
            region.Left = region.Right = csbi.srWindow.Right - 2;
        else if (region.Top < csbi.srWindow.Top)
            region.Top = region.Bottom = csbi.srWindow.Top + 1;
        else if (region.Bottom >= csbi.srWindow.Bottom)
            region.Top = region.Bottom = csbi.srWindow.Bottom - 2;

        // If not at a screen border, continue, otherwise beep.
        else
            continue;
        Beep((ci.Char.AsciiChar - 'A') * 100, 175);
    }
    // _endthread given to terminate
    _endthread();
}

按下任意鍵結束範例應用程式。

下列範例程式碼示範如何使用 透過同步處理 API WaitForSingleObject 傳回 _beginthreadex 的執行緒控制碼。 主執行緒會等待第二個執行緒終止後,再繼續執行。 當第二個執行緒呼叫 _endthreadex 時,它會導致其執行緒物件進入訊號狀態,這可讓主要執行緒繼續執行。 您無法使用 _beginthread_endthread 來完成,因為 _endthread 呼叫 CloseHandle 會終結執行緒物件,然後才能將它設定為訊號狀態。

// crt_begthrdex.cpp
// compile with: /MT
#include <windows.h>
#include <stdio.h>
#include <process.h>

unsigned Counter;
unsigned __stdcall SecondThreadFunc( void* pArguments )
{
    printf( "In second thread...\n" );

    while ( Counter < 1000000 )
        Counter++;

    _endthreadex( 0 );
    return 0;
}

int main()
{
    HANDLE hThread;
    unsigned threadID;

    printf( "Creating second thread...\n" );

    // Create the second thread.
    hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID );

    // Wait until second thread terminates. If you comment out the line
    // below, Counter will not be correct because the thread has not
    // terminated, and Counter most likely has not been incremented to
    // 1000000 yet.
    WaitForSingleObject( hThread, INFINITE );
    printf( "Counter should be 1000000; it is-> %d\n", Counter );
    // Destroy the thread object.
    CloseHandle( hThread );
}
Creating second thread...
In second thread...
Counter should be 1000000; it is-> 1000000

另請參閱