_beginthread
, _beginthreadex
Erstellt einen Thread.
Syntax
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
);
Parameter
start_address
Startadresse einer Routine, die die Ausführung eines neuen Threads beginnt. For _beginthread
, the calling convention is either __cdecl
(for native code) or __clrcall
(for managed code). For _beginthreadex
, the calling convention is either __stdcall
(for native code) or __clrcall
(for managed code).
stack_size
Stapelgröße für einen neuen Thread oder 0.
arglist
Argumentliste, die an einen neuen Thread übergeben werden soll, oder NULL
.
Security
Zeiger auf eine SECURITY_ATTRIBUTES
Struktur, die bestimmt, ob das zurückgegebene Handle von untergeordneten Prozessen geerbt werden kann. Ist Security
dies der Fall NULL
, kann der Handle nicht geerbt werden.
initflag
Flags, die den anfänglichen Zustand eines neuen Threads steuern. Legen Sie initflag
"0" fest, um sofort ausgeführt zu werden, oder um CREATE_SUSPENDED
den Thread in einem angehaltenen Zustand zu erstellen. Verwenden Sie ResumeThread
diese Option, um den Thread auszuführen. Legen Sie initflag
die STACK_SIZE_PARAM_IS_A_RESERVATION
Kennzeichnung fest, die als anfängliche Reservegröße des Stapels in Byte verwendet werden soll stack_size
. Wenn dieses Flag nicht angegeben ist, stack_size
gibt die Commit-Größe an.
thrdaddr
Zeigt auf eine 32-Bit-Variable, die den Threadbezeichner empfängt. Wenn dies der Wert ist NULL
, wird er nicht verwendet.
Rückgabewert
Bei Erfolg gibt jede dieser Funktionen ein Handle für den neu erstellten Thread zurück. Wenn der neu erstellte Thread allerdings zu schnell beendet wird, gibt _beginthread
möglicherweise kein gültiges Handle zurück. (Weitere Informationen finden Sie in der Diskussion im Abschnitt "Anmerkungen".) Gibt bei einem Fehler _beginthread
-1L zurück und errno
wird festgelegt EAGAIN
, wenn zu viele Threads vorhanden sind, ob EINVAL
das Argument ungültig ist oder die Stapelgröße falsch ist oder ob EACCES
nicht genügend Ressourcen vorhanden sind (z. B. Arbeitsspeicher). Bei einem Fehler gibt _beginthreadex
0 zurück, und errno
sowie _doserrno
werden festgelegt.
Wenn start_address
den Wert NULL
annimmt, wird der Handler für ungültige Parameter aufgerufen, wie in Parameter Validation. Wenn die weitere Ausführung zugelassen wird, stellen diese Funktionen errno
auf EINVAL
ein und geben -1 zurück.
Weitere Informationen zu diesen und anderen Rückgabecodes finden Sie unter , , _doserrno
, _sys_errlist
und _sys_nerr
.errno
Weitere Informationen finden uintptr_t
Sie unter "Standardtypen".
Hinweise
Mit der _beginthread
-Funktion wird ein Thread erstellt, der die Ausführung einer Routine bei start_address
beginnt. Die Routine bei start_address
muss die __cdecl
-Aufrufkonvention (für systemeigenen Code) oder die __clrcall
-Aufrufkonvention (für verwalteten Code) verwenden und sollte keinen Rückgabewert besitzen. Wenn der Thread von dieser Routine zurückgegeben wird, wird er automatisch beendet. Weitere Informationen zu Threads finden Sie unter Multithreading-Unterstützung für ältere Code (Visual C++).For more information about threads, see Multithreading support for older code (Visual C++).
_beginthreadex
ähnelt der Win32-API CreateThread
genauer als _beginthread
dies der Fall ist. _beginthreadex
unterscheidet sich auf folgende Weise von _beginthread
:
_beginthreadex
hat drei weitere Parameter:initflag
, ,Security
undthreadaddr
. Der neue Thread kann in einem unterbrochenen Zustand mit einer angegebenen Sicherheit erstellt werden, und es kann darauf mithilfe desthrdaddr
-Threadbezeichners zugegriffen werden.Die Routine bei
start_address
, die an_beginthreadex
übergeben wird, muss__stdcall
(für systemeigenen Code) oder__clrcall
(für verwalteten Code) verwenden und einen Threadbeendigungscode zurückgeben._beginthreadex
gibt bei Fehler "0" anstatt "-1L" zurück.Ein von der Verwendung
_beginthreadex
erstellter Thread wird durch einen Aufruf beendet_endthreadex
.
Mit der _beginthreadex
-Funktion können Sie die Erstellung des Threads besser steuern, als es mit _beginthread
möglich ist. Die _endthreadex
-Funktion ist auch flexibler. Sie können z. B. mit _beginthreadex
, Sicherheitsinformationen verwenden, den Ausgangszustand des Threads festlegen (ausgeführt oder angehalten) und den Threadbezeichner des neu erstellten Threads abrufen. Sie können auch das Threadhandle verwenden, das von _beginthreadex
den Synchronisierungs-APIs zurückgegeben wird, die Sie nicht verwenden _beginthread
können.
_beginthreadex
ist sicherer zu verwenden als _beginthread
. Wenn der Thread, der von _beginthread
generiert wird, schnell beendet, könnte das Handle, das dem Aufrufer von _beginthread
zurückgegeben wird, ungültig sein oder auf einem anderen Thread zeigen. Das von dem Aufrufer zurückgegebene _beginthreadex
Handle muss jedoch vom Aufrufer _beginthreadex
geschlossen werden, daher ist es garantiert ein gültiger Handle, wenn _beginthreadex
kein Fehler zurückgegeben wurde.
Sie können einen Thread aufrufen _endthread
oder _endthreadex
explizit beenden. Sie _endthreadex
wird jedoch automatisch aufgerufen, wenn der Thread von der Routine zurückgegeben wird, _endthread
die als Parameter übergeben wird. Das Beenden eines Threads durch Aufruf von _endthread
oder _endthreadex
stellt die korrekte Wiederherstellung der dem Thread zugeordneten Ressourcen sicher.
_endthread
schließt den Threadziehpunkt automatisch, während dies nicht der Fall _endthreadex
ist. Wenn Sie daher den Threadhandle verwenden _beginthread
und _endthread
nicht explizit schließen, indem Sie die Win32-API CloseHandle
aufrufen. Dieses Verhalten unterscheidet sich von der Win32-API ExitThread
.
Hinweis
Rufen Sie für eine mit "Libcmt.lib" verknüpfte ausführbare Datei die ExitThread
-Win32-API nicht auf, damit Sie das Laufzeitsystem nicht an der Freigabe von zugeordneten Ressourcen hindern. _endthread
und _endthreadex
geben zugeordnete Threadressourcen frei und rufen dann ExitThread
auf.
Die Speicherbelegung des Stapels wird vom Betriebssystem behandelt, wenn entweder _beginthread
oder _beginthreadex
aufgerufen wird. Sie müssen die Adresse des Threadstapels an keine dieser Funktionen übergeben. Außerdem kann das stack_size
-Argument "0" sein. In diesem Fall wird vom Betriebssystem der gleiche Wert wie vom Stapel verwendet, der für den Hauptthread angegeben wird.
arglist
ist ein Parameter, der an den neu erstellten Thread übergeben werden soll. In der Regel ist es die Adresse eines Datenelements, z. B. eine Zeichenfolge. arglist
kann sein NULL
, wenn dies nicht erforderlich ist, aber _beginthread
einen _beginthreadex
bestimmten Wert für die Übergabe an den neuen Thread erhalten muss. Alle Threads werden beendet, sobald ein Thread abort
, exit
, _exit
oder ExitProcess
aufruft.
Das Gebietsschema des neuen Threads wird mithilfe der globalen globalen Gebietsschemainformationen pro Prozess initialisiert. Wenn das Gebietsschema pro Thread durch einen Aufruf _configthreadlocale
aktiviert ist (entweder global oder nur für neue Threads), kann der Thread sein Gebietsschema unabhängig von anderen Threads durch Aufrufen setlocale
oder _wsetlocale
ändern. Threads, die nicht über das Gebietsschema-Flag pro Thread verfügen, können sich auf die Gebietsschemainformationen in allen anderen Threads auswirken, die auch nicht über das Gebietsschema-Flag pro Thread verfügen, und auch alle neu erstellten Threads. Weitere Informationen finden Sie unter Locale.
Für /clr
Code _beginthread
und _beginthreadex
jeweils über zwei Überladungen. Eine verwendet einen systemeigenen Aufrufkonventionsfunktionszeiger, und der andere verwendet einen __clrcall
Funktionszeiger. Die erste Überladung ist nicht anwendungssicher und wird nie sein. Wenn Sie Code schreiben /clr
, müssen Sie sicherstellen, dass der neue Thread die richtige Anwendungsdomäne eingibt, bevor er auf verwaltete Ressourcen zugreift. Sie können dies z. B. mithilfe von call_in_appdomain
. Die zweite Überladung ist anwendungsdomänensicher. Der neu erstellte Thread beendet immer in der Anwendungsdomäne des Aufrufers von _beginthread
oder _beginthreadex
.
Standardmäßig gilt der globale Zustand dieser Funktion für die Anwendung. Wie Sie dieses Verhalten ändern, erfahren Sie unter Globaler Status in der CRT.
Anforderungen
Routine | Erforderlicher Header |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
Weitere Informationen zur Kompatibilität finden Sie unter Kompatibilität.
Libraries
Nur Multithread-Versionen von C-Laufzeitbibliotheken .
Zum Verwenden von _beginthread
oder _beginthreadex
muss die Anwendung mit einer Multithreadanwendung der C-Laufzeitbibliotheken verknüpft werden.
Beispiele
Im folgenden Beispiel werden _beginthread
und _endthread
verwendet.
// 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();
}
Drücken Sie zum Beenden der Beispielanwendung eine beliebige Taste.
Der folgende Beispielcode veranschaulicht, wie Sie das threadhandle verwenden können, das mit _beginthreadex
der Synchronisierungs-API WaitForSingleObject
zurückgegeben wird. Der Hauptthread wartet auf das Beenden des zweiten Threads, bevor er fortsetzt. Wenn der zweite Thread aufgerufen wird _endthreadex
, wechselt das Threadobjekt in den signalierten Zustand, wodurch der primäre Thread weiter ausgeführt werden kann. Es kann nicht mit _beginthread
und _endthread
, weil _endthread
Aufrufe CloseHandle
, die das Threadobjekt zerstört, bevor es auf den signalierten Zustand festgelegt werden kann.
// 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