Freigeben über


_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 NULLannimmt, 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_errlistund _sys_nerr.errno

Weitere Informationen finden uintptr_tSie unter "Standardtypen".

Hinweise

Mit der _beginthread -Funktion wird ein Thread erstellt, der die Ausführung einer Routine bei start_addressbeginnt. 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, , Securityund threadaddr. Der neue Thread kann in einem unterbrochenen Zustand mit einer angegebenen Sicherheit erstellt werden, und es kann darauf mithilfe des thrdaddr-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 _beginthreadkö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 _beginthreadexgeschlossen 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 _endthreadnicht 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 ExitThreadauf.

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, _exitoder ExitProcessaufruft.

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 _beginthreadexmuss die Anwendung mit einer Multithreadanwendung der C-Laufzeitbibliotheken verknüpft werden.

Beispiele

Im folgenden Beispiel werden _beginthread und _endthreadverwendet.

// 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();
}

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 WaitForSingleObjectzurü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

Siehe auch