Partager via


_beginthread, _beginthreadex

Crée un thread.

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
);

Paramètres

start_address
Adresse de début d'une routine qui commence l'exécution d'un nouveau thread. Pour _beginthread, la convention d’appel est soit __cdecl (pour le code natif) soit __clrcall (pour le code managé). Pour _beginthreadex, la convention d’appel est soit __stdcall (pour le code natif) soit __clrcall (pour le code managé).

stack_size
Taille de la pile d'un nouveau thread ou 0.

arglist
Liste d’arguments à passer à un nouveau thread, ou NULL.

Security
Pointeur vers une SECURITY_ATTRIBUTES structure qui détermine si le handle retourné peut être hérité par les processus enfants. Si Security c’est NULLle cas, le handle ne peut pas être hérité.

initflag
Indicateurs qui contrôlent l'état initial d'un nouveau thread. Définissez initflag la valeur 0 pour s’exécuter immédiatement ou pour CREATE_SUSPENDED créer le thread dans un état suspendu ; utilisez-le ResumeThread pour exécuter le thread. Définissez l’indicateur initflag à utiliser stack_size comme taille de réserve initiale de la pile en octets ; si cet indicateur n’est pas spécifié, stack_size spécifie la taille de validation.STACK_SIZE_PARAM_IS_A_RESERVATION

thrdaddr
Pointe vers une variable 32 bits qui reçoit l'identificateur du thread. Si c’est NULLle cas, il n’est pas utilisé.

Valeur retournée

En cas de réussite, chacune de ces fonctions retourne un handle au nouveau thread. Toutefois, si le nouveau thread se ferme trop rapidement, _beginthread peut ne pas retourner un handle valide. (Consultez la discussion dans la section Remarques.) Sur une erreur, _beginthread retourne -1L et errno est défini EAGAIN sur s’il y a trop de threads, si EINVAL l’argument n’est pas valide ou si la taille de la pile est incorrecte, ou si EACCES des ressources insuffisantes (telles que la mémoire) sont insuffisantes. En cas d'erreur, _beginthreadex retourne 0 et errno et _doserrno sont définis.

Si start_address c’est NULLle cas, le gestionnaire de paramètres non valide est appelé, comme décrit dans la validation des paramètres. Si l'exécution est autorisée à se poursuivre, ces fonctions définissent errno avec la valeur EINVAL et retournent -1.

Pour plus d’informations sur ces codes de retour et d’autres codes de retour, consultez , , _sys_errlist_doserrnoet _sys_nerr.errno

Pour plus d’informations sur uintptr_t, consultez types Standard.

Notes

La fonction _beginthread crée un thread qui commence l'exécution d'une routine à start_address. La routine à start_address doit utiliser la convention d'appel __cdecl (pour le code natif) ou __clrcall (pour le code managé) et ne doit pas avoir de valeur de retour. Lorsque le thread revient de cette routine, il est arrêté automatiquement. Pour plus d’informations sur les threads, consultez la prise en charge du multithreading pour le code plus ancien (Visual C++).

_beginthreadex ressemble à l’API Win32 CreateThread plus étroitement que cela _beginthread . _beginthreadex diffère de _beginthread des manières suivantes :

  • _beginthreadex a trois paramètres supplémentaires : initflag, Securityet threadaddr. Le nouveau thread peut être créé dans un état suspendu, avec une sécurité spécifiée, et il est accessible à l'aide de thrdaddr, qui représente l'identificateur du thread.

  • La routine à start_address passée à _beginthreadex doit utiliser la convention d'appel __stdcall (pour le code natif) ou __clrcall (pour le code managé) et doit retourner un code de sortie de thread.

  • _beginthreadex retourne 0 en cas d'échec plutôt que -1L.

  • Un thread créé à l’aide _beginthreadex est arrêté par un appel à _endthreadex.

La fonction _beginthreadex vous permet de mieux contrôler la création du thread que _beginthread . La fonction _endthreadex est également plus flexible. Par exemple, avec _beginthreadex, vous pouvez utiliser les informations de sécurité, définir l'état initial du thread (en cours d'exécution ou suspendu) et obtenir l'identificateur du thread nouvellement créé. Vous pouvez également utiliser le handle de thread retourné par _beginthreadex les API de synchronisation, que vous ne pouvez pas faire avec _beginthread.

_beginthreadex est plus sûr d’utiliser que _beginthread. Si le thread généré par _beginthread se ferme rapidement, le handle retourné à l'appelant de _beginthread peut être non valide ou pointer sur un autre thread. Toutefois, le handle retourné par _beginthreadex doit être fermé par l’appelant, _beginthreadexde sorte qu’il est garanti qu’il soit un handle valide s’il _beginthreadex n’a pas retourné d’erreur.

Vous pouvez appeler _endthread ou _endthreadex mettre fin explicitement à un thread ; toutefois, _endthread ou _endthreadex est appelé automatiquement lorsque le thread retourne à partir de la routine passée en tant que paramètre. La fin d'un thread par un appel à _endthread ou _endthreadex permet de garantir une récupération correcte des ressources allouées pour le thread.

_endthread ferme automatiquement le handle de thread, alors que ce n’est pas le cas _endthreadex . Par conséquent, lorsque vous utilisez _beginthread et _endthreadque vous ne fermez pas explicitement le handle de thread en appelant l’API Win32 CloseHandle . Ce comportement diffère de l’API Win32 ExitThread .

Remarque

Pour un fichier exécutable lié à Libcmt.lib, n'appelez pas l'API Win32 ExitThread afin de ne pas empêcher le système runtime de récupérer les ressources allouées. _endthread et _endthreadex récupèrent les ressources de thread allouées, puis appellent ExitThread.

Le système d'exploitation gère l'allocation de la pile lorsque _beginthread ou _beginthreadex est appelé ; vous n'êtes pas obligé de passer l'adresse de la pile des threads à l'une ou l'autre de ces fonctions. En outre, l'argument stack_size peut être 0, auquel cas le système d'exploitation utilise la même valeur que la pile spécifiée pour le thread principal.

arglist est un paramètre à passer au thread nouvellement créé. En règle générale, il s’agit de l’adresse d’un élément de données, telle qu’une chaîne de caractères. arglist peut être NULL s’il n’est pas nécessaire, mais _beginthread doit _beginthreadex avoir une valeur à passer au nouveau thread. Tous les threads sont arrêtés si un thread appelle abort, exit, _exit, ou ExitProcess.

Les paramètres régionaux du nouveau thread sont initialisés à l’aide des informations locales globales par processus. Si les paramètres régionaux par thread sont activés par un appel à _configthreadlocale (globalement ou pour les nouveaux threads uniquement), le thread peut modifier ses paramètres régionaux indépendamment des autres threads en appelant setlocale ou _wsetlocale. Les threads qui n’ont pas l’indicateur de paramètres régionaux par thread peuvent affecter les informations de paramètres régionaux dans tous les autres threads qui n’ont pas non plus l’indicateur de paramètres régionaux par thread, ainsi que tous les threads nouvellement créés. Pour plus d’informations, consultez Locale.

Pour /clr le code, _beginthread et _beginthreadex chacun a deux surcharges. L’un prend un pointeur de fonction de convention d’appel natif, et l’autre prend un pointeur de __clrcall fonction. La première surcharge n’est pas sécurisée par le domaine d’application et ne sera jamais. Si vous écrivez /clr du code, vous devez vous assurer que le nouveau thread entre dans le domaine d’application approprié avant d’accéder aux ressources managées. Vous pouvez le faire, par exemple, à l’aide call_in_appdomainde . La seconde surcharge est sécurisée au niveau du domaine d'application ; le nouveau thread finit toujours dans le domaine d'application de l'appelant de _beginthread ou _beginthreadex.

Par défaut, l’état global de cette fonction est limité à l’application. Pour modifier ce comportement, consultez État global dans le CRT.

Spécifications

Routine En-tête requis
_beginthread <process.h>
_beginthreadex <process.h>

Pour plus d’informations sur la compatibilité, consultez Compatibility.

Bibliothèques

Uniquement les versions multithread des bibliothèques Runtime C .

Pour utiliser _beginthread ou _beginthreadex, l'application doit être liée à l'une des bibliothèques multithread Runtime C.

Exemples

L'exemple suivant utilise _beginthread et _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 = &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();
}

Appuyez sur n'importe quelle touche pour fermer l'exemple d'application.

L’exemple de code suivant montre comment utiliser le handle de thread retourné par _beginthreadex l’API WaitForSingleObjectde synchronisation. Le thread principal attend que le second thread se termine avant de continuer. Lorsque le deuxième thread appelle _endthreadex, son objet thread passe à l’état signalé, ce qui permet au thread principal de continuer à s’exécuter. Il ne peut pas être effectué avec _beginthread et _endthread, car _endthread les appels CloseHandle, qui détruit l’objet thread avant de pouvoir être défini sur l’état signalé.

// 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

Voir aussi