_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호출 규칙은 (네이티브 코드의 경우) 또는 __clrcall (관리 코드의 경우) 중 하나 __cdecl 입니다. 의 경우 _beginthreadex호출 규칙은 (네이티브 코드의 경우) 또는 __clrcall (관리 코드의 경우) 중 하나 __stdcall 입니다.

stack_size
새 스레드의 스택 크기 또는 0입니다.

arglist
새 스레드에 전달할 인수 목록 또는 NULL.

Security
반환된 핸들을 SECURITY_ATTRIBUTES 자식 프로세스에서 상속할 수 있는지 여부를 결정하는 구조체에 대한 포인터입니다. 이 NULL경우 Security 핸들을 상속할 수 없습니다.

initflag
새 스레드의 초기 상태를 제어하는 플래그입니다. 즉시 실행하거나 일시 중단된 상태에서 스레드를 만들려면 CREATE_SUSPENDED 0으로 설정합니다initflag. 스레드를 실행하는 데 사용합니다ResumeThread. STACK_SIZE_PARAM_IS_A_RESERVATION 스택의 초기 예약 크기(바이트)로 사용하도록 stack_size 플래그로 설정합니다initflag. 이 플래그를 지정 stack_size 하지 않으면 커밋 크기를 지정합니다.

thrdaddr
스레드 식별자를 수신하는 32비트 변수를 가리킵니다. NULL이 경우 사용되지 않습니다.

반환 값

성공하면 이러한 함수가 각각 새로 만든 스레드에 대한 핸들을 반환하지만 새로 만든 스레드가 너무 빨리 종료되는 경우 _beginthread 가 올바른 핸들을 반환할 수 없습니다. (설명 섹션의 토론을 참조하세요.) 오류 _beginthread 발생 시 -1L을 반환하고 errno 스레드가 너무 많은 경우, EINVAL 인수가 잘못되었거나 스택 크기가 올바르지 EACCES 않거나 리소스가 부족한 경우(예: 메모리)로 설정 EAGAIN 됩니다. 오류 발생 시 _beginthreadex 에서 0을 반환하고 errno_doserrno 가 설정됩니다.

이 경우 start_address 매개 변수 유효성 검사에 설명된 대로 잘못된 매개 변수 처리기가 호출NULL됩니다. 계속해서 실행하도록 허용한 경우 이러한 함수는 errnoEINVAL 로 설정하고 -1을 반환합니다.

이러한 코드 및 기타 반환 코드에 대한 자세한 내용은 , _doserrno_sys_nerr_sys_errlist를 참조하세요.errno

자세한 내용은 표준 형식을 uintptr_t참조 하세요.

설명

_beginthread 함수는 start_address에서 루틴 실행을 시작하는 스레드를 만듭니다. start_address 의 루틴은 __cdecl (네이티브 코드) 또는 __clrcall (관리 코드) 호출 규칙을 사용해야 하며, 반환 값이 없어야 합니다. 스레드가 해당 루틴에서 반환되면 자동으로 종료됩니다. 스레드 에 대한 자세한 내용은 이전 코드에 대한 다중 스레딩 지원(Visual C++)을 참조하세요.

_beginthreadex 는 Win32 CreateThread API와 유사 _beginthread 합니다. _beginthreadex_beginthread 의 차이점은 다음과 같습니다.

  • _beginthreadex에는 세 개의 매개 변수가 initflagSecuritythreadaddr더 있습니다. 새 스레드는 지정된 보안 또는 일시 중단된 상태로 만들 수 있으며, 스레드 식별자인 thrdaddr를 사용하여 액세스할 수 있습니다.

  • start_address 에 전달되는 _beginthreadex 의 루틴은 __stdcall (네이티브 코드) 또는 __clrcall (관리 코드) 호출 규칙을 사용해야 하며, 스레드 종료 코드를 반환해야 합니다.

  • _beginthreadex 는 실패 시 -1L이 아닌 0을 반환합니다.

  • 를 사용하여 _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 API를 호출하지 마세요. _endthread_endthreadex 는 할당된 스레드 리소스를 회수한 다음 ExitThread를 호출합니다.

_beginthread 또는 _beginthreadex가 호출되면 운영 체제가 스택 할당을 처리합니다. 스레드 스택의 주소를 이러한 함수 중 하나에 전달할 필요가 없습니다. 또한 stack_size 인수는 0일 수 있습니다. 이 경우 운영 체제는 주 스레드에 지정된 스택과 동일한 값을 사용합니다.

arglist 는 새로 만든 스레드에 전달되는 매개 변수입니다. 일반적으로 문자열과 같은 데이터 항목의 주소입니다. arglist 는 필요하지 않은 경우일 NULL 수 있지만 _beginthread_beginthreadex 새 스레드에 전달하려면 몇 가지 값을 제공해야 합니다. 임의의 스레드가 abort, exit, _exit또는 ExitProcess를 호출하면 모든 스레드가 종료됩니다.

새 스레드의 로캘은 프로세스별 전역 현재 로캘 정보를 사용하여 초기화됩니다. 스레드별 로캘이 호출 _configthreadlocale (전역적으로 또는 새 스레드에만 해당)으로 사용하도록 설정된 경우 스레드는 호출 setlocale 하거나 _wsetlocale호출하여 다른 스레드와 독립적으로 로캘을 변경할 수 있습니다. 스레드별 로캘 플래그 집합이 없는 스레드는 스레드별 로캘 플래그가 설정되지 않은 다른 모든 스레드의 로캘 정보 및 새로 만든 모든 스레드에도 영향을 줄 수 있습니다. 자세한 내용은 Locale을 참조하세요.

코드의 _beginthread_beginthreadex 경우 /clr 각각 두 개의 오버로드가 있습니다. 하나는 네이티브 호출 규칙 함수 포인터를 사용하고 다른 하나는 함수 포인터를 __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();
}

아무 키나 눌러 샘플 애플리케이션을 종료합니다.

다음 샘플 코드에서는 동기화 APIWaitForSingleObject를 사용하여 반환 _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

참고 항목