次の方法で共有


_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 構造体へのポインター。この構造体は、返されたハンドルを子プロセスが継承できるかどうかを決定します。 SecurityNULL場合、ハンドルを継承することはできません。

initflag
新しいスレッドの初期状態を制御するフラグ。 即時に実行するには initflag を 0 に設定し、一時停止状態でスレッドを作成するには CREATE_SUSPENDED に設定します。スレッドを実行するには、ResumeThread を使用します。 initflagSTACK_SIZE_PARAM_IS_A_RESERVATIONフラグに設定して、スタックの初期予約サイズとしてstack_sizeをバイト単位で使用します。このフラグが指定されていない場合は、コミット サイズstack_size指定します。

thrdaddr
スレッド識別子を受け取る 32 ビット変数へのポインター。 NULL の場合は使用されません。

戻り値

成功すると、各関数は新しく作成されたスレッドのハンドルを返します。ただし、新しく作成されたスレッドの終了が早すぎる場合、 _beginthread は有効なハンドルを返さないことがあります (「解説」セクションの説明を参照してください)。エラーが発生した場合、 _beginthread は -1L を返し、 errno はスレッドが多すぎる場合は EAGAIN に設定され、引数が無効であるかスタック サイズが正しくない場合は EINVAL するか、リソースが不足している場合 (メモリなど) が EACCES されます。 エラーが発生すると、 _beginthreadex は 0 を返します。このとき errno_doserrno が設定されます。

start_addressNULL の場合は、「パラメーターの検証」で説明されているように、無効なパラメーター ハンドラーが呼び出されます。 実行の継続が許可された場合、これらの関数は errnoEINVAL に設定し、-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 には、 initflagSecuritythreadaddrの 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 に何らかの値を指定する必要があります。 いずれかのスレッドが abortexit_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 = &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();
}

任意のキーを押してサンプル アプリケーションを終了します。

_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

関連項目