_beginthread
, _beginthreadex
Tworzy wątek.
Składnia
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
);
Parametry
start_address
Rozpocznij adres procedury, która rozpoczyna wykonywanie nowego wątku. W przypadku _beginthread
metody konwencja wywoływania to __cdecl
(dla kodu natywnego) lub __clrcall
(dla kodu zarządzanego). W przypadku _beginthreadex
metody konwencja wywoływania to __stdcall
(dla kodu natywnego) lub __clrcall
(dla kodu zarządzanego).
stack_size
Rozmiar stosu dla nowego wątku lub 0.
arglist
Lista argumentów, która ma zostać przekazana do nowego wątku lub NULL
.
Security
Wskaźnik do SECURITY_ATTRIBUTES
struktury, która określa, czy zwrócony uchwyt może być dziedziczony przez procesy podrzędne. Jeśli Security
parametr ma NULL
wartość , nie można dziedziczyć uchwytu.
initflag
Flagi kontrolujące stan początkowy nowego wątku. Ustaw initflag
wartość 0, aby uruchomić natychmiast lub CREATE_SUSPENDED
utworzyć wątek w stanie wstrzymania; użyj polecenia ResumeThread
, aby wykonać wątek. Ustaw initflag
wartość na STACK_SIZE_PARAM_IS_A_RESERVATION
flagę, która ma być używana stack_size
jako początkowy rozmiar rezerwowy stosu w bajtach. Jeśli ta flaga nie jest określona, stack_size
określa rozmiar zatwierdzenia.
thrdaddr
Wskazuje zmienną 32-bitową, która otrzymuje identyfikator wątku. Jeśli jest NULL
to wartość , nie jest używana.
Wartość zwracana
W przypadku powodzenia każda z tych funkcji zwraca dojście do nowo utworzonego wątku; jeśli jednak nowo utworzony wątek kończy się zbyt szybko, _beginthread
może nie zwrócić prawidłowego uchwytu. (Zobacz dyskusję w sekcji Uwagi). W przypadku błędu _beginthread
zwraca wartość -1L i errno
jest ustawiona na EAGAIN
wartość , jeśli istnieje zbyt wiele wątków, do EINVAL
tego, czy argument jest nieprawidłowy, czy rozmiar stosu jest niepoprawny lub EACCES
jeśli nie ma wystarczających zasobów (takich jak pamięć). W przypadku błędu _beginthreadex
zwraca wartość 0 i errno
_doserrno
są ustawione.
Jeśli start_address
parametr ma NULL
wartość , wywoływana jest nieprawidłowa procedura obsługi parametrów, zgodnie z opisem w temacie Weryfikacja parametrów. Jeśli wykonanie może kontynuować, te funkcje są ustawione errno
na EINVAL
wartość i zwracają wartość -1.
Aby uzyskać więcej informacji na temat tych i innych kodów powrotnych, zobacz errno
, _doserrno
, _sys_errlist
i _sys_nerr
.
Aby uzyskać więcej informacji na temat uintptr_t
programu , zobacz Typy standardowe.
Uwagi
Funkcja _beginthread
tworzy wątek, który rozpoczyna wykonywanie procedury w .start_address
W procedurze start_address
należy użyć __cdecl
(dla kodu natywnego) lub __clrcall
(dla kodu zarządzanego) wywołującej konwencję i nie powinna mieć wartości zwracanej. Gdy wątek powróci z tej procedury, zostanie on automatycznie zakończony. Aby uzyskać więcej informacji na temat wątków, zobacz Obsługa wielowątków dla starszego kodu (Visual C++).
_beginthreadex
bardziej przypomina interfejs API Win32 CreateThread
, niż _beginthread
to robi. _beginthreadex
różni się od _beginthread
następujących sposobów:
_beginthreadex
Ma trzy więcej parametrów:initflag
,Security
ithreadaddr
. Nowy wątek można utworzyć w stanie wstrzymania z określonymi zabezpieczeniami i można uzyskać do nich dostęp za pomocą elementuthrdaddr
, który jest identyfikatorem wątku.Procedury przekazywane w
start_address
celu_beginthreadex
użycia__stdcall
(dla kodu natywnego) lub__clrcall
(dla kodu zarządzanego) wywołującej konwencję i muszą zwrócić kod zakończenia wątku._beginthreadex
Zwraca wartość 0 w przypadku awarii, a nie -1L.Wątek utworzony przy użyciu
_beginthreadex
jest przerywany przez wywołanie metody_endthreadex
.
Funkcja _beginthreadex
zapewnia większą kontrolę nad tym, jak jest tworzony wątek._beginthread
Funkcja jest również bardziej elastyczna _endthreadex
. Na przykład w programie _beginthreadex
można użyć informacji zabezpieczających, ustawić początkowy stan wątku (uruchomiony lub zawieszony) i pobrać identyfikator wątku nowo utworzonego wątku. Możesz również użyć dojścia wątku zwróconego za pomocą _beginthreadex
interfejsów API synchronizacji, których nie można wykonać za pomocą _beginthread
polecenia .
_beginthreadex
jest bezpieczniejszy do użycia niż _beginthread
. Jeśli wątek, który jest generowany przez _beginthread
wyjście szybko, uchwyt zwrócony do obiektu wywołującego _beginthread
może być nieprawidłowy lub wskaż inny wątek. Jednak uchwyt zwrócony przez _beginthreadex
element musi zostać zamknięty przez obiekt wywołujący _beginthreadex
elementu , więc musi być prawidłowym uchwytem, jeśli _beginthreadex
nie zwrócił błędu.
Można wywołać _endthread
lub _endthreadex
jawnie przerwać wątek; jednak lub _endthreadex
jest wywoływany automatycznie, _endthread
gdy wątek powraca z procedury przekazanej jako parametr. Przerywanie wątku za pomocą wywołania _endthread
metody lub _endthreadex
pomaga zapewnić poprawne odzyskiwanie zasobów przydzielonych dla wątku.
_endthread
automatycznie zamyka uchwyt wątku, natomiast _endthreadex
nie. W związku z tym w przypadku używania elementów _beginthread
i _endthread
nie zamykaj jawnie dojścia wątku przez wywołanie interfejsu API Win32 CloseHandle
. To zachowanie różni się od interfejsu API Win32 ExitThread
.
Uwaga
W przypadku pliku wykonywalnego połączonego z biblioteką Libcmt.lib nie należy wywoływać interfejsu API Win32 ExitThread
, aby nie uniemożliwić systemowi czasu wykonywania odzyskanie przydzielonych zasobów. _endthread
i _endthreadex
odzyskać przydzielone zasoby wątku, a następnie wywołać metodę ExitThread
.
System operacyjny obsługuje alokację stosu, gdy _beginthread
jest wywoływany lub _beginthreadex
; nie musisz przekazywać adresu stosu wątków do jednej z tych funkcji. Ponadto stack_size
argument może mieć wartość 0, w takim przypadku system operacyjny używa tej samej wartości co stos określony dla wątku głównego.
arglist
jest parametrem, który ma zostać przekazany do nowo utworzonego wątku. Zazwyczaj jest to adres elementu danych, taki jak ciąg znaków. arglist
może być NULL
taka, jeśli nie jest potrzebna, ale _beginthread
_beginthreadex
musi mieć pewną wartość do przekazania do nowego wątku. Wszystkie wątki są przerywane, jeśli jakikolwiek wątek wywołuje abort
, exit
, _exit
lub ExitProcess
.
Ustawienia regionalne nowego wątku są inicjowane przy użyciu globalnych informacji o bieżących ustawieniach regionalnych dla poszczególnych procesów. Jeśli ustawienia regionalne dla każdego wątku są włączone przez wywołanie metody _configthreadlocale
(globalnie lub tylko dla nowych wątków), wątek może zmienić ustawienia regionalne niezależnie od innych wątków przez wywołanie setlocale
lub _wsetlocale
. Wątki, które nie mają ustawionej flagi ustawień regionalnych dla wątku, mogą mieć wpływ na informacje o ustawieniach regionalnych we wszystkich innych wątkach, które również nie mają ustawionej flagi ustawień regionalnych dla wątku, a także wszystkie nowo utworzone wątki. Aby uzyskać więcej informacji, zobacz Ustawienia regionalne.
W przypadku /clr
kodu _beginthread
każdy _beginthreadex
z nich ma dwa przeciążenia. Jeden z nich przyjmuje natywny wskaźnik funkcji konwencji wywoływania, a drugi przyjmuje __clrcall
wskaźnik funkcji. Pierwsze przeciążenie nie jest bezpieczne dla domeny aplikacji i nigdy nie będzie. Jeśli piszesz /clr
kod, musisz upewnić się, że nowy wątek wchodzi w poprawną domenę aplikacji przed uzyskaniem dostępu do zasobów zarządzanych. Można to zrobić, na przykład za pomocą polecenia call_in_appdomain
. Drugie przeciążenie jest bezpieczne dla domeny aplikacji; nowo utworzony wątek będzie zawsze znajdować się w domenie aplikacji obiektu wywołującego _beginthread
elementu lub _beginthreadex
.
Domyślnie stan globalny tej funkcji jest zakresem aplikacji. Aby zmienić to zachowanie, zobacz Stan globalny w CRT.
Wymagania
Procedura | Wymagany nagłówek |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
Aby uzyskać więcej informacji o zgodności, zobacz Zgodność.
Biblioteki
Tylko wielowątkowe wersje bibliotek czasu wykonywania języka C.
Aby można było używać _beginthread
programu lub _beginthreadex
, aplikacja musi łączyć się z jedną z wielowątkowych bibliotek czasu wykonywania języka C.
Przykłady
W poniższym przykładzie użyto elementów _beginthread
i _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 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();
}
Naciśnij dowolny, aby zakończyć przykładową aplikację.
Poniższy przykładowy kod pokazuje, jak można użyć dojścia wątku zwróconego za pomocą _beginthreadex
interfejsu API WaitForSingleObject
synchronizacji . Główny wątek czeka na zakończenie drugiego wątku przed kontynuowaniem. Gdy drugi wątek wywołuje _endthreadex
element , powoduje, że jego obiekt wątku przechodzi do stanu sygnalizowanego, co umożliwia kontynuowanie działania wątku podstawowego. Nie można go wykonać za pomocą _beginthread
poleceń i _endthread
, ponieważ _endthread
wywołuje CloseHandle
metodę , która niszczy obiekt wątku, zanim będzie można ustawić go na stan zasygnalizowany.
// 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