_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
新しいスレッドの実行を開始するルーチンの開始アドレス。 _beginthread
の場合、呼び出し規約は__cdecl
(ネイティブ コードの場合) または__clrcall
(マネージド コードの場合) です。 _beginthreadex
の場合、呼び出し規約は__stdcall
(ネイティブ コードの場合) または__clrcall
(マネージド コードの場合) です。
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
はスレッドが多すぎる場合は EAGAIN
に設定され、引数が無効であるかスタック サイズが正しくない場合は EINVAL
するか、リソースが不足している場合 (メモリなど) が EACCES
されます。 エラーが発生すると、 _beginthreadex
は 0 を返します。このとき errno
と _doserrno
が設定されます。
start_address
が NULL
の場合は、「パラメーターの検証」で説明されているように、無効なパラメーター ハンドラーが呼び出されます。 実行の継続が許可された場合、これらの関数は errno
を EINVAL
に設定し、-1 を返します。
これらのリターン コードとその他のリターン コードについては、「errno
、_doserrno
、_sys_errlist
、_sys_nerr
」を参照してください。
uintptr_t
の詳細については、「標準型」を参照してください。
解説
_beginthread
関数は、 start_address
で指定されるルーチンの実行を開始するスレッドを作成します。 start_address
にあるルーチンは、 __cdecl
(ネイティブ コードの場合) または __clrcall
(マネージド コードの場合) の呼び出し規約を使用する必要があり、戻り値を持つことはできません。 スレッドがそのルーチンから戻ると、自動的に終了します。 スレッドの詳細については、「 古いコードのマルチスレッドのサポート (Visual C++)」を参照してください。
_beginthreadex
は、_beginthread
よりも Win32 CreateThread
API に似ています。 _beginthreadex
は、次の点が _beginthread
とは異なります。
_beginthreadex
には、initflag
、Security
、threadaddr
の 3 つのパラメーターがあります。 セキュリティを指定し、一時停止状態で新しいスレッドを作成できます。新しいスレッドには、スレッド識別子thrdaddr
を使用してアクセスできます。start_address
に渡される_beginthreadex
のルーチンは、__stdcall
(ネイティブ コードの場合) または__clrcall
(マネージド コードの場合) の呼び出し規約を使用する必要があり、スレッドの終了コードを返す必要があります。_beginthreadex
は、エラーの発生時に -1L ではなく 0 を返します。_beginthreadex
を使用して作成したスレッドは、_endthreadex
の呼び出しで終了します。
_beginthreadex
関数は、スレッドの作成において _beginthread
関数よりも制御を詳細に行うことができます。 また、 _endthreadex
関数も、より柔軟性があります。 たとえば、 _beginthreadex
では、セキュリティ情報の使用、スレッドの初期状態 (実行中または一時停止中) の設定、新しく作成されたスレッドのスレッド識別子の取得を行うことができます。 _beginthread
では実行できない同期 API で_beginthreadex
によって返されるスレッド ハンドルを使用することもできます。
_beginthreadex
は、 _beginthread
よりも安全に使用できます。 _beginthread
によって生成されたスレッドの終了が早すぎると、 _beginthread
の呼び出し元に返されるハンドルが無効になる可能性や、別のスレッドを指す可能性があります。 ただし、 _beginthreadex
によって返されるハンドルは、 _beginthreadex
の呼び出し元によって閉じる必要があるため、 _beginthreadex
がエラーを返さなかった場合は有効なハンドルであることが保証されます。
_endthread
または _endthreadex
を明示的に呼び出してスレッドを終了できます。ただし、_endthread
または _endthreadex
は、パラメーターとして渡されたルーチンからスレッドが戻ると自動的に呼び出されます。 _endthread
または _endthreadex
を呼び出してスレッドを終了すると、スレッドに割り当てられていたリソースを確実に解放できます。
_endthread
はスレッド ハンドルを自動的に閉じますが、 _endthreadex
は閉じません。 このため、_beginthread
および _endthread
を使用するときには、Win32 CloseHandle
API を呼び出してスレッド ハンドルを明示的に終了しないでください。 この動作は、Win32 ExitThread
API とは異なります。
Note
Libcmt.lib にリンクされている実行可能ファイルでは、Win32 の ExitThread
API を呼び出さないでください。呼び出すと、割り当てられたリソースをランタイム システムで再利用することができなくなります。 _endthread
と _endthreadex
は、割り当てられているスレッド リソースを解放し、 ExitThread
を呼び出します。
_beginthread
または _beginthreadex
が呼び出されると、オペレーティング システムがスタックの割り当てを処理します。したがって、スレッド スタックのアドレスをこれらの関数に渡す必要はありません。 また、引数 stack_size
に 0 を指定すると、オペレーティング システムはメイン スレッドに対して指定したスタックと同じ値を使用します。
arglist
は新しく作成したスレッドに渡すパラメーターです。 通常は、文字列などのデータ項目のアドレスです。 arglist
は、必要ない場合は NULL
できますが、新しいスレッドに渡すには、 _beginthread
と _beginthreadex
に何らかの値を指定する必要があります。 いずれかのスレッドが abort
、 exit
、 _exit
、または ExitProcess
を呼び出すと、すべてのスレッドが終了します。
新しいスレッドのロケールは、プロセスごとのグローバルな現在のロケール情報を使用して初期化されます。 _configthreadlocale
の呼び出しにより、スレッドごとのロケールが (グローバルまたは新しいスレッドのみに対して) 有効になっている場合は、スレッドで setlocale
または _wsetlocale
を呼び出すことで、そのスレッドのロケールを他のスレッドとは関係なく変更できます。 スレッドごとのロケール フラグが設定されていないスレッドは、スレッドごとのロケール フラグが設定されていない他のすべてのスレッドのロケール情報と、新しく作成されたすべてのスレッドに影響を与える可能性があります。 詳細については、「 Locale」を参照してください。
/clr
コードについて、_beginthread
と _beginthreadex
にはそれぞれ 2 つのオーバーロードが含まれます。 1 つはネイティブの呼び出し規約関数ポインターを受け取り、もう 1 つは __clrcall
関数ポインターを受け取ります。 最初のオーバーロードはアプリケーションドメインセーフではなく、決して安全ではありません。 /clr
コードを記述する場合は、マネージド リソースにアクセスする前に、新しいスレッドが正しいアプリケーション ドメインに入っていることを確認する必要があります。 たとえば、 call_in_appdomain
を使用して行うことができます。 2 番目のオーバーロードはアプリケーション ドメイン セーフであり、新しく作成されたスレッドは、必ず _beginthread
または _beginthreadex
の呼び出し元のアプリケーション ドメインで終了します。
既定では、この関数のグローバル状態の適用対象は、アプリケーションになります。 この動作を変更するには、「CRT でのグローバル状態」を参照してください。
要件
ルーチンによって返される値 | 必須ヘッダー |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
互換性の詳細については、「 Compatibility」を参照してください。
ライブラリ
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();
}
任意のキーを押してサンプル アプリケーションを終了します。
_beginthreadex
から返されたスレッド ハンドルを同期 API WaitForSingleObject
と共に使用する方法を次のサンプル コードで示します。 メイン スレッドは、2 番目のスレッドが終了するのを待って処理を継続します。 2 番目のスレッドが _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