_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
結構的指標,判斷傳回的句柄是否可以由子進程繼承。 如果 Security
為 NULL
,則無法繼承句柄。
initflag
控制新執行緒之初始狀態的旗標。 將 設定 initflag
為 0 以立即執行,或設定 CREATE_SUSPENDED
為 以暫止狀態建立線程;使用 ResumeThread
來執行線程。 設定 initflag
為 STACK_SIZE_PARAM_IS_A_RESERVATION
旗標,以位元組為單位作為 stack_size
堆疊的初始保留大小;如果未指定此旗標, stack_size
則指定認可大小。
thrdaddr
指向接收執行緒識別項的 32 位元變數。 NULL
如果是 ,則不會使用。
傳回值
如果成功,則上述每個函式都會將控制代碼傳回至新建立的執行緒;然而,如果新建立的執行緒太快結束, _beginthread
可能無法傳回有效的控制代碼 (請參閱一節中的討論。發生錯誤時,_beginthread
會傳回 -1L,如果errno
線程太多,EINVAL
則設定為 ;如果自變數無效或堆棧大小不正確,或資源不足(例如記憶體)則設定EAGAIN
為 EACCES
。 發生錯誤時, _beginthreadex
會傳回 0,並設定 errno
和 _doserrno
。
如果 start_address
為 NULL
,將會叫用無效參數處理常式,如參數驗證 (部分機器翻譯) 中所述。 如果允許繼續執行,這些函式會將 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
還有三個參數:initflag
、Security
和threadaddr
。 新執行緒可以在指定安全性的暫停狀態下建立,並且可以使用執行緒識別項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
必須提供一些值,才能傳遞至新的線程。 如果有任何執行緒呼叫 abort
、 exit
、 _exit
或 ExitProcess
,就會結束所有的執行緒。
新線程的地區設定是使用每個進程全域目前地區設定資訊初始化。 如果呼叫 _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 = ¶m;
// 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, ®ion);
// 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