_beginthread
, _beginthreadex
Vytvoří vlákno.
Syntaxe
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
Zahajte adresu rutiny, která zahájí spuštění nového vlákna. Pro _beginthread
volání je konvence buď __cdecl
(pro nativní kód), nebo __clrcall
(pro spravovaný kód). Pro _beginthreadex
volání je konvence buď __stdcall
(pro nativní kód), nebo __clrcall
(pro spravovaný kód).
stack_size
Velikost zásobníku pro nové vlákno nebo 0
arglist
Seznam argumentů, který se má předat novému vláknu, nebo NULL
.
Security
Ukazatel na SECURITY_ATTRIBUTES
strukturu, která určuje, zda vrácený popisovač lze dědit podřízenými procesy. Pokud Security
ano NULL
, popisovač nemůže být zděděný.
initflag
Označí příznaky, které řídí počáteční stav nového vlákna. Nastavte initflag
na hodnotu 0, která se má spustit okamžitě, nebo vytvořte CREATE_SUSPENDED
vlákno v pozastaveném stavu. Slouží ResumeThread
ke spuštění vlákna. Nastavte initflag
příznak, který STACK_SIZE_PARAM_IS_A_RESERVATION
se má použít stack_size
jako počáteční velikost zásobníku v bajtech. Pokud tento příznak není zadaný, stack_size
určuje velikost potvrzení.
thrdaddr
Odkazuje na 32bitovou proměnnou, která přijímá identifikátor vlákna. Pokud je NULL
, nepoužívá se.
Vrácená hodnota
V případě úspěchu vrátí každá z těchto funkcí popisovač nově vytvořenému vláknu; Pokud se však nově vytvořené vlákno příliš rychle ukončí, _beginthread
nemusí vrátit platný popisovač. (Viz diskuze v části Poznámky.) Při chybě _beginthread
vrátí hodnotu -1L a errno
je nastavena na EAGAIN
to, zda existuje příliš mnoho vláken, pokud EINVAL
je argument neplatný nebo je nesprávná velikost zásobníku nebo EACCES
pokud není k dispozici dostatek prostředků (například paměti). Při chybě _beginthreadex
vrátí hodnotu 0 a errno
_doserrno
nastaví se.
Pokud start_address
je NULL
, je vyvolána neplatná obslužná rutina parametru, jak je popsáno v ověření parametru. Pokud je spuštění povoleno pokračovat, tyto funkce jsou nastavené errno
na EINVAL
hodnotu -1 a vrátí hodnotu -1.
Další informace o těchto a dalších návratových kódech naleznete v tématu , , , a_sys_nerr
. _sys_errlist
_doserrno
errno
Další informace o uintptr_t
typu Standard naleznete v tématu Standardní typy.
Poznámky
Funkce _beginthread
vytvoří vlákno, které zahájí provádění rutiny v start_address
. Rutina start_address
musí používat __cdecl
konvenci volání (pro nativní kód) nebo __clrcall
(pro spravovaný kód) a nesmí mít žádnou návratnou hodnotu. Když se vlákno vrátí z této rutiny, automaticky se ukončí. Další informace o vláknech najdete v tématu Podpora multithreadingu pro starší kód (Visual C++).
_beginthreadex
podobá rozhraní API Win32 CreateThread
blíže, než _beginthread
tomu tak je. _beginthreadex
se liší od _beginthread
následujících způsobů:
_beginthreadex
má tři další parametry:initflag
,Security
athreadaddr
. Nové vlákno lze vytvořit v pozastaveném stavu se zadaným zabezpečením a lze k němu přistupovat pomocíthrdaddr
identifikátoru vlákna.Rutina
start_address
, která_beginthreadex
je předána, musí používat__stdcall
konvenci volání (pro nativní kód) nebo__clrcall
(pro spravovaný kód) a musí vrátit ukončovací kód vlákna._beginthreadex
vrátí hodnotu 0 při selhání místo -1L.Vlákno vytvořené pomocí je
_beginthreadex
ukončeno voláním_endthreadex
.
Funkce _beginthreadex
poskytuje větší kontrolu nad tím, jak se vlákno vytváří, než _beginthread
dělá. Funkce _endthreadex
je také flexibilnější. Můžete například _beginthreadex
použít informace o zabezpečení, nastavit počáteční stav vlákna (spuštěno nebo pozastaveno) a získat identifikátor vlákna nově vytvořeného vlákna. Můžete také použít popisovač vlákna vrácený rozhraními _beginthreadex
API pro synchronizaci, se kterými se nemůžete zabývat _beginthread
.
_beginthreadex
je bezpečnější používat než _beginthread
. Pokud se vlákno vygenerované rychlým ukončením _beginthread
ukončí, může být popisovač vrácený volajícímu _beginthread
neplatný nebo odkazovat na jiné vlákno. Popisovač vrácený _beginthreadex
volajícím _beginthreadex
však musí být uzavřen , takže je zaručeno, že je platný popisovač, pokud _beginthreadex
nevrátil chybu.
Vlákno můžete volat _endthread
nebo _endthreadex
explicitně ukončit, ale nebo _endthreadex
se volá automaticky, _endthread
když se vlákno vrátí z rutiny, která se předává jako parametr. Ukončení vlákna voláním _endthread
nebo _endthreadex
pomáhá zajistit správné obnovení prostředků přidělených pro vlákno.
_endthread
automaticky zavře popisovač vlákna, zatímco _endthreadex
ne. Proto při použití _beginthread
a _endthread
, nezavírejte explicitně popisovač vlákna voláním rozhraní API Win32 CloseHandle
. Toto chování se liší od rozhraní API Win32 ExitThread
.
Poznámka:
U spustitelného souboru propojeného s knihovnou Libcmt.lib nevolejte rozhraní API win32 ExitThread
, abyste nezabráněli systému za běhu v uvolnění přidělených prostředků. _endthread
a _endthreadex
uvolněte přidělené prostředky vlákna a pak volejte ExitThread
.
Operační systém zpracovává přidělení zásobníku, když je _beginthread
volána nebo _beginthreadex
je volána. Do některé z těchto funkcí nemusíte předávat adresu zásobníku vláken. Kromě toho stack_size
může být argument 0, v takovém případě operační systém používá stejnou hodnotu jako zásobník zadaný pro hlavní vlákno.
arglist
je parametr, který se má předat nově vytvořenému vláknu. Obvykle se jedná o adresu datové položky, například řetězec znaků. arglist
může být NULL
v případě, že není potřeba, ale _beginthread
_beginthreadex
musí mít hodnotu, která se má předat novému vláknu. Všechna vlákna jsou ukončena, pokud jakékoli volání abort
vlákna , exit
, _exit
, nebo ExitProcess
.
Národní prostředí nového vlákna se inicializuje pomocí globálních informací o národním prostředí pro jednotlivé procesy. Pokud je národní prostředí pro jednotlivá vlákna povoleno voláním _configthreadlocale
(globálně nebo pouze pro nová vlákna), může vlákno změnit své národní prostředí nezávisle na jiných vláknech voláním setlocale
nebo _wsetlocale
. Vlákna, která nemají nastavený příznak národního prostředí pro jednotlivá vlákna, můžou mít vliv na informace o národním prostředí ve všech ostatních vláknech, která také nemají nastavenou příznak národního prostředí pro jednotlivá vlákna, a také všechna nově vytvořená vlákna. Další informace naleznete v tématu Národní prostředí.
Pro /clr
kód _beginthread
a _beginthreadex
každý z nich má dvě přetížení. Jeden přebírá nativní ukazatel funkce konvence volání a druhý přebírá __clrcall
ukazatel funkce. První přetížení není bezpečné pro doménu aplikace a nikdy nebude. Pokud píšete /clr
kód, musíte před přístupem ke spravovaným prostředkům zajistit, aby nové vlákno zadalo správnou doménu aplikace. Můžete to udělat například pomocí .call_in_appdomain
Druhé přetížení je bezpečné pro doménu aplikace; nově vytvořené vlákno vždy skončí v doméně aplikace volajícího _beginthread
nebo _beginthreadex
.
Ve výchozím nastavení je globální stav této funkce vymezen na aplikaci. Chcete-li toto chování změnit, přečtěte si téma Globální stav v CRT.
Požadavky
Rutina | Požadovaný hlavičkový soubor |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
Další informace o kompatibilitě najdete v tématu Kompatibilita.
Knihovny
Pouze vícevláknové verze knihoven runtime jazyka C.
Chcete-li použít _beginthread
nebo _beginthreadex
, musí aplikace propojit s jednou z vícevláknových knihoven jazyka C za běhu.
Příklady
Následující příklad používá _beginthread
a _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();
}
Stisknutím libovolné klávesy ukončete ukázkovou aplikaci.
Následující ukázkový kód ukazuje, jak můžete použít popisovač vlákna vrácený rozhraním _beginthreadex
API WaitForSingleObject
pro synchronizaci . Hlavní vlákno čeká na ukončení druhého vlákna, než bude pokračovat. Když druhé vlákno volá _endthreadex
, způsobí, že jeho vlákno objekt přejde do signalovaného stavu, což umožňuje primární vlákno pokračovat ve spuštění. Nelze to provést s _beginthread
a _endthread
, protože _endthread
volání CloseHandle
, která zničí vlákno objekt předtím, než lze nastavit na signalovaný stav.
// 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