Поделиться через


_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, дескриптор не наследуется. Должно быть NULL для приложений Windows 95.

  • initflag
    Флаги, управляющие начальным состоянием нового потока. Задайте для параметра initflag флаг 0, чтобы выполнять запуск немедленно, или флаг CREATE_SUSPENDED, чтобы создать поток в приостановленном состоянии. Для выполнения потока используйте функцию ResumeThread. Задайте для параметра initflag флаг STACK_SIZE_PARAM_IS_A_RESERVATION, чтобы использовать stack_size в качестве начального зарезервированного размера стека (в байтах); если этот флаг не указан, 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, and _sys_nerr.

Дополнительные сведения о uintptr_t см. в разделе Стандартные типы.

Заметки

Функция _beginthread создает поток, который начинает выполнение процедуры в start_address. В процедуре start_address необходимо использовать __cdecl (для машинного кода) или соглашение о вызовах __clrcall (для управляемого кода); там не должно быть возвращаемого значения. При возврате потока из этой процедуры он завершается автоматически. Дополнительные сведения о потоках см. в разделе Поддержка многопоточности для устаревшего кода (Visual C++).

_beginthreadex напоминает API Win32 CreateThread больше, чем _beginthread. _beginthreadex имеет следующие отличия от _beginthread:

  • _beginthreadex имеет три дополнительных параметра: initflag, security и threadaddr. Новый поток можно создать в приостановленном состоянии (с заданной безопасностью), а доступ к нему можно осуществлять с помощью thrdaddr, который является идентификатором потока.

  • Процедура в start_address, передаваемая атрибуту _beginthreadex, должна использовать __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 не следует явно закрывать дескриптор потока вызовом API Win32 CloseHandle. Этот режим отличается от API Win32 ExitThread.

Примечание

Для исполняемого файла, связанного с Libcmt.lib, не следует вызывать API Win32 ExitThread, чтобы не помешать системе времени выполнения освобождать выделенные ресурсы._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 = &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 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();
}

Чтобы закрыть приложение-пример, нажмите любую клавишу.

В следующем примере кода показано, как использовать дескриптор потока, возвращаемый функцией _beginthreadex, с помощью API синхронизации WaitForSingleObject. Основной поток ожидает завершения другого потока, прежде чем продолжить. Если второй поток вызывает _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

См. также

Ссылки

Управление процессами и средой

_endthread, _endthreadex

abort

exit, _exit

GetExitCodeThread