_beginthread, _beginthreadex
스레드를 만듭니다.
중요
이 API는 Windows 런타임에서 실행되거나 clr:pure 스위치를 사용하여 컴파일된 응용 프로그램에서 사용할 수 없습니다.자세한 내용은 CRT 함수는 /ZW 옵션을 지원하지 않음을 참조하세요.
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인 경우 핸들을 상속할 수 없습니다. Windows 95 응용 프로그램에서 NULL이어야 합니다.initflag
새 스레드의 초기 상태를 제어하는 플래그입니다. 즉시 실행하려면 initflag를 0으로 설정하고 스레드를 일시 중단된 상태로 만들려면 CREATE_SUSPENDED로 설정합니다. 스레드를 실행하려면 ResumeThread를 사용합니다. stack_size를 스택의 초기 예약 크기(바이트)로 사용하려면 initflag를 STACK_SIZE_PARAM_IS_A_RESERVATION으로 설정합니다. 이 플래그를 지정하지 않으면 stack_size는 커밋 크기를 지정합니다.thrdaddr
스레드 식별자를 수신하는 32비트 변수를 가리킵니다. NULL인 경우는 사용되지 않습니다.
반환 값
성공하면 이러한 함수가 각각 새로 만든 스레드에 대한 핸들을 반환하지만 새로 만든 스레드가 너무 빨리 종료되는 경우 _beginthread가 올바른 핸들을 반환할 수 없습니다. 설명 단원의 설명을 참조하십시오. 오류로 인해 _beginthread가 -1L로 반환되고 errno는 스레드가 많을 경우 EAGAIN에 설정되고, 인수가 잘못되거나 스택 크기가 잘못된 경우EINVAL에 설정되며, 리소스(즉, 메모리)가 부족할 경우EACCES에 설정됩니다. 오류 발생 시 _beginthreadex에서 0을 반환하고 errno 및 _doserrno가 설정됩니다.
startaddress가 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이 있습니다. 새 스레드는 지정된 보안 또는 일시 중단된 상태로 만들 수 있으며, 스레드 식별자인 thrdaddr를 사용하여 액세스할 수 있습니다.
_beginthreadex에 전달되는 start_address의 루틴은 __stdcall(네이티브 코드) 또는 __clrcall(관리 코드) 호출 규칙을 사용해야 하며, 스레드 종료 코드를 반환해야 합니다.
_beginthreadex는 실패 시 -1L이 아닌 0을 반환합니다.
_beginthreadex를 사용하여 생성된 스레드는 _endthreadex에 대한 호출로 종료됩니다.
_beginthreadex 함수를 사용하면 _beginthread보다 스레드를 만드는 방법을 더 자세하게 제어할 수 있습니다. _endthreadex 함수는 더 유연하기도 합니다. 예를 들어, _beginthreadex를 사용하면 보안 정보를 사용하고 스레드의 초기 상태(실행 중 또는 일시 중단됨)를 설정하고 새로 만든 스레드의 스레드 식별자를 가져올 수 있습니다. _beginthread로 작업할 수 없는 동기화 API로 _beginthreadex에서 반환된 스레드 핸들을 사용할 수도 있습니다.
_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 각각에 두 개의 오버로드가 있습니다. 하나는 네이티브 호출 규칙 함수 포인터를 사용하고 다른 하나는 __clrcall 함수 포인터를 사용합니다. 첫 번째 오버로드는 응용 프로그램 도메인에 안전하지 않고 어떤 방법으로도 안전할 수 없습니다. 혼합형 또는 순수형 코드를 작성하는 경우 새 스레드가 관리되는 리소스에 액세스하기 전에 올바른 응용 프로그램 도메인에 들어가는지 확인해야 합니다. 이 작업은 call_in_appdomain 함수 등을 사용하여 수행할 수 있습니다. 두 번째 오버로드는 응용 프로그램 도메인에 영향을 주지 않습니다. 새로 생성된 스레드는 항상 _beginthread 또는 _beginthreadex 호출자의 응용 프로그램 도메인에서 종료됩니다.
요구 사항
루틴 |
필수 헤더 |
---|---|
_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 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를 호출하면 스레드 개체가 신호를 받은 상태로 전환됩니다. 그러면 기본 스레드를 계속 실행할 수 있습니다. _endthread는 신호된 상태로 설정되기 전에 스레드 개체를 소멸하는 CloseHandle을 호출하기 때문에 이 작업은 _beginthread 및 _endthread로 수행할 수 없습니다.
// 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 );
}
해당 .NET Framework 항목
System::Threading::Thread::Start