_beginthread, _beginthreadex
创建线程。
重要事项 |
---|
此 API 不能在运行时的窗口执行的应用程序。有关更多信息,请参见 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 结构的指针返回的句柄是否可由子进程继承。如果为 null,句柄不能被继承。必须为空对于 Windows 95 应用程序。Initflag
新的挂起的线程 (运行的0 或 CREATE_SUSPENDED ) 的初始状态;使用 ResumeThread 执行线程。Thrdaddr
指向接收线程标识符的 32 位变量。在不使用情况下,可能是 NULL它。
返回值
如果成功,这些函数都返回一个处理到新创建的线程;但是,因此,如果新创建的线程太快速退出,_beginthread 可能不返回有效句柄 (参见中 Remarks 部分的讨论)。_beginthread 返回在错误的 -1L,在 errno 设置为 EAGAIN,如果具有多个线程情况下,对 EINVAL,如果无效的参数或堆栈大小不正确,或者为 EACCES 后足够的资源 (如内存)。在 errno 和 _doserrno 设置情况下,_beginthreadex 返回 0 在错误。
如果 startaddress 是 NULL,无效参数调用处理程序,如 参数验证所述。如果执行允许继续,对 EINVAL 的这些功能集 errno 并且返回 -1。
有关这些属性和其他的更多信息返回代码示例,请参见 _doserrno、errno、_sys_errlist 和_sys_nerr。
有关 uintptr_t的更多信息,请参见 标准类型。
备注
_beginthread 函数开始创建实例的执行在 start_address的线程。在 start_address 的实例必须使用 (对于托管代码) 调用约定 __cdecl (对于本机代码) 或 __clrcall,应没有返回值。当线程从该实例时返回,它会自动停止。有关线程的更多信息,请参见 多线程处理。
_beginthreadex 比 _beginthread 非常类似于 Win32 CreateThread API。_beginthreadex 与 _beginthread 以下方面有所不同:
_beginthreadex 有三个参数:initflag、security和 threadaddr。新线程处于挂起状态后,以指定安全 (仅限 Windows NT) 使用 thrdaddr,因此,可访问,是线程标识符。
在 start_address 的实例传递给 _beginthreadex 必须使用 (对于托管代码) 调用约定 __stdcall (对于本机代码) 或 __clrcall,并且必须返回线程退出代码。
_beginthreadex 返回 0 在失败,而不是 -1L。
在 _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 API CloseHandle 显式关闭线程句柄。此行为与 Win32 API ExitThread 不同。
说明 |
---|
对于使用 Libcmt.lib 链接的可执行文件,不要调用 Win32 API; ExitThread 这样可防止该运行时系统恢复已分配的资源。_endthread 和分发 _endthreadex 回收线程资源然后调用 ExitThread。 |
操作系统句柄堆栈的分配,当 _beginthread 或 _beginthreadex 调用;您不需要传入线程堆栈的地址到其中一种功能。此外,在中,在为主线程情况下,使用值和堆栈所指定的操作系统 stack_size 参数可以为 0。
arglist 是传递的参数传递给新创建的线程。它通常是 dataitem 的地址,例如字符字符串。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 *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();
}
按任意键关闭
下面的代码示例演示如何使用与同步 API WaitForSingleObject的 _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 );
}
.NET Framework 等效项
System::Threading::Thread::Start