_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 = ¶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 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();
}
Чтобы закрыть приложение-пример, нажмите любую клавишу.
В следующем примере кода показано, как использовать дескриптор потока, возвращаемый функцией _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