_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
인 경우 Parameter Validation를 참조하세요. 계속해서 실행하도록 허용한 경우 이러한 함수는 errno
를 EINVAL
로 설정하고 -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
에는 세 개의 매개 변수가initflag
Security
threadaddr
더 있습니다. 새 스레드는 지정된 보안 또는 일시 중단된 상태로 만들 수 있으며, 스레드 식별자인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 = ¶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();
}
아무 키나 눌러 샘플 애플리케이션을 종료합니다.
다음 샘플 코드에서는 동기화 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