_beginthread、_beginthreadex
更新 : 2007 年 11 月
スレッドを作成します。
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
);
パラメータ
start_address
新規スレッドの実行を起動するルーチンの開始アドレス。_beginthread の呼び出し規約は __cdecl または __clrcall で、_beginthreadex の呼び出し規約は __stdcall または __clrcall です。stack_size
新規スレッドのスタック サイズまたは 0。arglist
新規スレッドに渡される引数リストまたは NULL。security
SECURITY_ATTRIBUTES 構造体へのポインタ。この構造体は、返されたハンドルを子プロセスが継承できるかどうかを決定します。NULL を指定すると、ハンドルを継承できません。Windows 95 アプリケーションの場合は、NULL を指定する必要があります。initflag
新規スレッドの初期状態 (実行中は 0、一時停止中は CREATE_SUSPENDED)。スレッドを実行するには、ResumeThread を使用します。thrdaddr
スレッド識別子を受け取る 32 ビット変数へのポインタ。NULL の場合があり、その場合は使用されません。
戻り値
正常に処理されると、各関数は新規作成されたスレッドのハンドルを返します。ただし、新しく作成されたスレッドの終了が早すぎると、_beginthread が有効なハンドルを返さない場合があります (Remarks の説明を参照)。エラーが発生すると、_beginthread は 1L を返します。このとき errno は、スレッドの数が多すぎる場合は EAGAIN に設定され、引数が無効か、またはスタックのサイズが不適切な場合は EINVAL に設定されます。また、リソース不足 (メモリ不足など) の場合は EACCES に設定されます。_beginthreadex は、エラーが発生すると 0 を返します。この場合、errno と _doserrno が設定されます。
startaddress が NULL の場合は、「パラメータの検証」に説明されているように、無効なパラメータ ハンドラが呼び出されます。実行の継続が許可された場合、これらの関数は errno を EINVAL に設定し、-1 を返します。
リターン コードの詳細については、「_doserrno、errno、_sys_errlist、および _sys_nerr」を参照してください。
uintptr_t の詳細については、「基本データ型」を参照してください。
解説
_beginthread 関数は、ルーチンの実行を start_address で起動するスレッドを作成します。start_address で起動されるルーチンは、__cdecl 呼び出し規約を使用する必要があり、戻り値を持つことはできません。スレッドは、ルーチンから戻ると自動的に終了します。スレッドの詳細については、「マルチスレッド」を参照してください。
_beginthreadex は、_beginthread よりも Win32 CreateThread API に似ています。_beginthreadex は、次の点で _beginthread と異なります。
_beginthreadex には、initflag、security、および threadaddr の 3 つの追加パラメータがあります。セキュリティを指定し (Windows NT のみ)、一時停止状態で新規スレッドを作成できます。新規スレッドには、スレッド識別子 thrdaddr を使用してアクセスできます。
_beginthreadex に渡される start_address のルーチンは、__stdcall 呼び出し規約を使用し、スレッド終了コードを返す必要があります。
_beginthreadex は、エラー発生時に 1L ではなく、0 を返します。
_beginthreadex を使用して作成したスレッドは、_endthreadex の呼び出しで終了します。
_beginthreadex 関数は、スレッドの作成において _beginthread 関数よりも制御を詳細に行うことができます。また、_endthreadex 関数も、より柔軟性があります。たとえば、_beginthreadex では、セキュリティ情報の使用、スレッドの初期状態 (実行中または一時停止中) の設定、新規に作成したスレッドのスレッド識別子の取得を行うことができます。また、_beginthreadex が返すスレッド ハンドルを同期 API と共に使用することもできます。_beginthread では使用できません。
_beginthread よりも _beginthreadex を使用した方が安全です。_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 API を呼び出さないでください。呼び出すと、実行時システムは割り当てられたリソースを解放できなくなります。_endthread と _endthreadex は、割り当てられているスレッド リソースを解放し、ExitThread を呼び出します。 |
_beginthread または _beginthreadex が呼び出されると、オペレーティング システムがスタックの割り当てを処理します。したがって、スレッド スタックのアドレスをこれらの関数に渡す必要はありません。また、引数 stack_size に 0 を指定すると、オペレーティング システムはメイン スレッドに対して指定したスタックと同じ値を使用します。
arglist は新しく作成したスレッドに渡すパラメータです。通常、文字列などのデータ項目のアドレスを指定します。不要な場合は arglist に NULL を指定します。ただし、_beginthread と _beginthreadex には、新規スレッドに渡す値を指定する必要があります。いずれかのスレッドが abort、exit、_exit、または ExitProcess を呼び出すと、すべてのスレッドが終了します。
新しいスレッドのロケールは親スレッドから継承されます。_configthreadlocale を呼び出してスレッドごとのロケールが有効になっている場合 (グローバルまたは新規のスレッドのみ)、スレッドは setlocale または _wsetlocale を呼び出して親とは関係なくロケールを変更できます。詳細については、「ロケール」を参照してください。
混合コードと純粋なコードでは、_beginthread と _beginthreadex には共に 2 つのオーバーロードがあり、1 つはネイティブの呼び出し規約関数ポインタを受け取り、もう 1 つは __clrcall 関数ポインタを受け取ります。最初のオーバーロードは、アプリケーション ドメインセーフではなく、以降もそうなることはありません。混合コードまたは純粋なコードを記述する場合、新しいスレッドがマネージ リソースにアクセスする前に確実に正しいアプリケーション ドメインに入るようにする必要があります。そのためには、call_in_appdomain 関数 などを使用します。2 番目のオーバーロードはアプリケーション ドメイン セーフであり、新規作成されたスレッドは必ず _beginthread または _beginthreadex の呼び出し元のアプリケーション ドメインで終了します。
必要条件
ルーチン |
必須ヘッダー |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
互換性の詳細については、「C ランタイム ライブラリ」の「互換性」を参照してください。
ライブラリ
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 *ch );
void CheckKey( void *dummy );
/* GetRandom returns a random integer between min and max. */
#define GetRandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))
BOOL repeat = TRUE; /* Global repeat flag and video variable */
HANDLE hStdOut; /* Handle for console window */
CONSOLE_SCREEN_BUFFER_INFO csbi; /* Console information structure */
int main()
{
CHAR ch = 'A';
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
/* Get display screen's text row and column information. */
GetConsoleScreenBufferInfo( hStdOut, &csbi );
/* Launch CheckKey thread to check for terminating keystroke. */
_beginthread( CheckKey, 0, NULL );
/* Loop until CheckKey terminates program. */
while( repeat )
{
/* On first loops, launch character threads. */
_beginthread( Bounce, 0, (void *) (ch++) );
/* Wait one second between loops. */
Sleep( 1000L );
}
}
/* CheckKey - Thread to wait for a keystroke, then clear repeat flag. */
void CheckKey( void *dummy )
{
_getch();
repeat = 0; /* _endthread implied */
}
/* Bounce - Thread to create and and control a colored letter that moves
* around on the screen.
* Params: ch - the letter to be moved
*/
void Bounce( void *ch )
{
/* Generate letter and color attribute from thread argument. */
char blankcell = 0x20;
char blockcell = (char) ch;
BOOL first = TRUE;
COORD oldcoord, newcoord;
DWORD result;
/* Seed random number generator and get initial location. */
srand( _threadid );
newcoord.X = GetRandom( 0, csbi.dwSize.X - 1 );
newcoord.Y = GetRandom( 0, csbi.dwSize.Y - 1 );
while( repeat )
{
/* Pause between loops. */
Sleep( 100L );
/* Blank out our old position on the screen, and draw new letter. */
if( first )
first = FALSE;
else
WriteConsoleOutputCharacter( hStdOut, &blankcell, 1, oldcoord, &result );
WriteConsoleOutputCharacter( hStdOut, &blockcell, 1, newcoord, &result );
/* Increment the coordinate for next placement of the block. */
oldcoord.X = newcoord.X;
oldcoord.Y = newcoord.Y;
newcoord.X += GetRandom( -1, 1 );
newcoord.Y += GetRandom( -1, 1 );
/* Correct placement (and beep) if about to go off the screen. */
if( newcoord.X < 0 )
newcoord.X = 1;
else if( newcoord.X == csbi.dwSize.X )
newcoord.X = csbi.dwSize.X - 2;
else if( newcoord.Y < 0 )
newcoord.Y = 1;
else if( newcoord.Y == csbi.dwSize.Y )
newcoord.Y = csbi.dwSize.Y - 2;
/* If not at a screen border, continue, otherwise beep. */
else
continue;
Beep( ((char) ch - 'A') * 100, 175 );
}
/* _endthread given to terminate */
_endthread();
}
press any key to end
_beginthreadex から返されたスレッド ハンドルを同期 API WaitForSingleObject と共に使用する方法を次のサンプル コードで示します。メイン スレッドは、2 番目のスレッドが終了するのを待って処理を継続します。2 番目のスレッドが _endthreadex を呼び出すと、2 番目のスレッドのスレッド オブジェクトがシグナル状態になります。これにより、1 番目のスレッドの実行が継続されます。_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
.NET Framework の相当するアイテム
System::Threading::Thread::Start