_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 (для управляемого кода).

stack_size
Размер стека нового потока или 0.

arglist
Список аргументов, передаваемый в новый поток или NULL.

Security
Указатель на SECURITY_ATTRIBUTES структуру, которая определяет, может ли возвращаемый дескриптор наследоваться дочерними процессами. Если Security есть NULL, дескриптор не может быть унаследован.

initflag
Флаги, управляющие начальным состоянием нового потока. Установите initflag значение 0 для немедленного выполнения или CREATE_SUSPENDED создания потока в приостановленном состоянии; используется ResumeThread для выполнения потока. STACK_SIZE_PARAM_IS_A_RESERVATION Установите initflag для флага, который будет использоваться stack_size в качестве начального резервного размера стека в байтах; если этот флаг не указан, stack_size указывает размер фиксации.

thrdaddr
Указывает на 32-разрядную переменную, которая получает идентификатор потока. Если это NULLтак, он не используется.

Возвращаемое значение

В случае успеха каждая из этих функций возвращает дескриптор во вновь созданный поток; однако если вновь созданный поток выполняет выход слишком быстро, _beginthread может не возвращать допустимый дескриптор. (См. обсуждение в разделе "Примечания".) При ошибке _beginthread возвращает значение -1L и errno имеет EAGAIN значение, если имеется слишком много потоков, в EINVAL том случае, если аргумент недопустим или размер стека неверный, или EACCES если недостаточно ресурсов (например, памяти). При возникновении ошибки _beginthreadex возвращает 0, а errno и _doserrno заданы.

В противном start_addressNULLслучае вызывается недопустимый обработчик параметров, как описано в разделе "Проверка параметров". Если продолжение выполнения разрешено, эти функции устанавливают для errno значение EINVAL и возвращают -1.

Дополнительные сведения об этих и других кодах возврата см. в разделе errno, _doserrnoи _sys_nerr_sys_errlist.

Дополнительные сведения см. в 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 явно завершать поток. Однако или _endthreadex вызывается автоматически, когда поток возвращается из подпрограммы, _endthread передаваемой в качестве параметра. Остановка потока вызовом метода _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. Потоки, у которых нет набора флагов языкового стандарта для каждого потока, могут повлиять на сведения о языковом стандарте во всех остальных потоках, которые также не имеют набора флагов языкового стандарта для каждого потока, а также все созданные потоки. Дополнительные сведения см. в разделе Locale.

Для /clr кода _beginthread_beginthreadex и каждая из них имеет две перегрузки. Один принимает собственный указатель функции-соглашения, а другой принимает __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 = &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();
}

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

В следующем примере кода показано, как использовать дескриптор потока, возвращаемый _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 );
}
Creating second thread...
In second thread...
Counter should be 1000000; it is-> 1000000

См. также