_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(对于托管代码)。

Start address of a routine that begins execution of a new thread. For _beginthread, the calling convention is either __cdecl (for native code) or __clrcall (for managed code); for _beginthreadex, it is either __stdcall (for native code) or __clrcall (for managed code).

stack_size

新线程的堆栈大小,或为0。

Stack size for a new thread, or 0.

arglist

要传递给新线程的参数列表,或者为NULL。

Argument list to be passed to a new thread, or NULL.

Security

指向SECURITY_ATTRIBUTES结构的安全指针,该结构确定返回的句柄是否可以由子进程继承。如果Security为NULL,则不能继承该句柄。对于Windows 95应用程序,必须为NULL。

Pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If Security is NULL, the handle cannot be inherited. Must be NULL for Windows 95 applications.

initflag

用于控制新线程的初始状态的标志。将initflag设置为0可立即运行,或将CREATE_SUSPENDED设置为处于挂起状态的线程;使用ResumeThread执行线程。
将initflag设置为STACK_SIZE_PARAM_IS_A_RESERVATION标志,以使用stack_size作为堆栈的初始保留大小(以字节为单位);如果未指定此标志,则stack_size指定提交大小。

Flags that control the initial state of a new thread. Set initflag to 0 to run immediately, or to CREATE_SUSPENDED to create the thread in a suspended state; use ResumeThread to execute the thread. Set initflag to STACK_SIZE_PARAM_IS_A_RESERVATION flag to use stack_size as the initial reserve size of the stack in bytes; if this flag is not specified, stack_size specifies the commit size.

thrdaddr

指向接收线程标识符的32位变量。如果为NULL,则不使用。

Points to a 32-bit variable that receives the thread identifier. If it’s NULL, it’s not used.

返回值

如果成功,则这些函数中的每个函数都会为新创建的线程返回一个句柄。但是,如果新创建的线程退出太快,则_beginthread可能不会返回有效的句柄。(见备注部分的讨论。)上的错误,_beginthread返回-1L,并且将errno设置为EAGAIN如果有太多的线程,以EINVAL如果参数无效或堆栈大小是不正确,或EACCES如果没有足够的资源(例如内存)。发生错误时,_beginthreadex返回0,并设置了errno和_doserrno。

如果start_address为NULL,则将调用无效的参数处理程序,如Parameter Validation中所述。如果允许执行继续,这些函数会将errno设置为EINVAL并返回-1。

有关这些代码和其他返回代码的更多信息,请参见errno,_doserrno,_sys_errlist和_sys_nerr。

有关uintptr_t的更多信息,请参见“ 标准类型”。

备注

该_beginthread函数创建,在开始例行的执行线程START_ADDRESS。start_address处的例程必须使用__cdecl(对于本机代码)或__clrcall(对于托管代码)调用约定,并且不应具有任何返回值。当线程从该例程返回时,它将自动终止。有关线程的更多信息,请参见旧代码的多线程支持(Visual C ++)。

_beginthreadex类似的Win32 的CreateThread更加紧密地比API _beginthread一样。_beginthreadex与_beginthread在以下方面有所不同:

_beginthreadex具有三个附加参数:initflag,Security和threadaddr。可以以指定的安全性在挂起状态下创建新线程,并可以使用thrdaddr(线程标识符)对其进行访问。

传递给_beginthreadex的start_address上的例程必须使用__stdcall(对于本机代码)或__clrcall(对于托管代码)调用约定,并且必须返回线程退出代码。

_beginthreadex失败时返回0,而不是-1L。

使用_beginthreadex创建的线程会通过调用_endthreadex终止。

该_beginthreadex功能为您提供了更多的控制权的线程是如何创建比_beginthread一样。该_endthreadex功能也更加灵活。例如,使用_beginthreadex,您可以使用安全性信息,设置线程的初始状态(运行或暂停),并获取新创建的线程的线程标识符。您还可以将_beginthreadex返回的线程句柄与同步API一起使用,而_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必须提供一些值才能传递给新线程。如果任何线程调用中止,退出,_exit或ExitProcess,则终止所有线程。

通过使用每个进程的全局当前语言环境信息来初始化新线程的语言环境。如果通过调用_configthreadlocale(全局或仅用于新线程)启用了每个线程的语言环境,则该线程可以通过调用setlocale或_wsetlocale来独立于其他线程来更改其语言环境。没有设置每线程语言环境标志的线程会影响所有其他没有设置每线程语言环境标志的线程的语言环境信息,以及所有新创建的线程。有关更多信息,请参见语言环境。

对于/ clr代码,_beginthread和_beginthreadex每个都有两个重载。一个采用本机调用约定函数指针,而另一个采用__clrcall函数指针。第一次重载不是应用程序域安全的,永远不会。如果要编写/ clr代码,则必须确保新线程在访问托管资源之前进入正确的应用程序域。您可以例如通过使用call_in_appdomain Function来执行此操作。第二重载是应用程序域安全的;新创建的线程将始终以_beginthread或_beginthreadex的调用者的应用程序域结尾。

例子

以下示例使用_beginthread和_endthread。

// crt_BEGTHRD.C
// compile with: /MT /D "_X86_" /c
// processor: x86
#include 
#include     /* _beginthread, _endthread */
#include 
#include 
#include 

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();
}

以下示例代码演示了如何通过同步API WaitForSingleObject使用_beginthreadex返回的线程句柄。主线程在继续之前等待第二个线程终止。当第二个线程调用_endthreadex时,它将导致其线程对象进入信号状态。这允许主线程继续运行。不能使用_beginthread和_endthread来完成此操作,因为_endthread调用CloseHandle,它会在可以将其设置为信号状态之前破坏线程对象。

// crt_begthrdex.cpp
// compile with: /MT
#include 
#include 
#include 

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 );
}

参考自 _beginthread,_beginthreadex

你可能感兴趣的:(C++)