Partager via


_beginthread, _beginthreadex

Crée un thread.

Important

Cette API ne peut pas être utilisée dans les applications qui s'exécutent dans le Windows Runtime ou qui sont compilées à l'aide du commutateur clr:pure.Pour plus d'informations, voir Fonctions CRT non prises en charge avec /ZW.

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 __cdecl (pour le code natif) ou __clrcall (pour le code managé) ; pour _beginthreadex, il s'agit de __stdcall (pour le code natif) ou de __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 structure SECURITY_ATTRIBUTES qui détermine si le handle retourné peut être hérité par des processus enfants. Si Security est NULL, le handle ne peut pas être hérité. Doit avoir la valeur NULL pour les applications Windows 95.

  • initflag
    Indicateurs qui contrôlent l'état initial d'un nouveau thread. Affectez à initflag la valeur 0 pour une exécution immédiate ou l'indicateur CREATE_SUSPENDED pour créer le thread dans un état suspendu ; utilisez ResumeThread pour exécuter le thread. Affectez à initflag l'indicateur STACK_SIZE_PARAM_IS_A_RESERVATION pour 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.

  • thrdaddr
    Pointe vers une variable 32 bits qui reçoit l'identificateur du thread. Si sa valeur est NULL, il n'est pas utilisé.

Valeur de retour

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 Notes.) En cas d'erreur, _beginthread retourne -1L. Par ailleurs, errno prend la valeur EAGAIN s'il y a trop de threads, la valeur EINVAL si l'argument n'est pas valide ou si la taille de la pile est incorrecte, ou la valeur EACCES si les ressources sont insuffisantes (par exemple la mémoire). En cas d'erreur, _beginthreadex retourne 0 et errno et _doserrno sont définis.

Si startaddress a la valeur NULL, le gestionnaire de paramètre non valide est appelé, comme décrit dans Validation de paramètre. 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 autres, consultez errno, _doserrno, _sys_errlist et _sys_nerr.

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 retourne de cette routine, il est terminé automatiquement. Pour plus d'informations sur les threads, consultez Prise en charge du multithreading pour le code plus ancien (Visual C++).

_beginthreadex ressemble beaucoup plus à l'API Win32 CreateThread que _beginthread. _beginthreadex diffère de _beginthread des façons suivantes :

  • _beginthreadex a trois paramètres supplémentaires : initflag, security et 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 de _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 du thread qui est retourné par _beginthreadex avec les API de synchronisation, ce que vous ne pouvez pas faire avec _beginthread.

Il est préférable d'utiliser _beginthreadex au lieu de _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 de _beginthreadex. Il s'agit donc forcément d'un handle valide si _beginthreadex n'a pas renvoyé d'erreur.

Vous pouvez appeler _endthread ou _endthreadex explicitement pour terminer un thread ; toutefois, _endthread ou _endthreadex est appelé automatiquement lorsque le thread retourne de la routine passée comme 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, ce qui n'est pas le cas de _endthreadex. Par conséquent, quand vous utilisez _beginthread et _endthread, ne fermez pas explicitement le handle du thread en appelant l'API Win32 CloseHandle. Ce comportement est différent de celui de l'API Win32 ExitThread.

Notes

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 général, il s'agit de l'adresse d'un élément de donnée, tel qu'une chaîne de caractères. arglist peut être null s'il n'est pas nécessaire, mais _beginthread et _beginthreadex doivent 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 hérités de son thread parent. Si les paramètres régionaux par thread sont activés par un appel à _configthreadlocale (globalement ou pour de nouveaux threads uniquement), le thread peut modifier ses paramètres régionaux indépendamment de son parent en appelant setlocale ou _wsetlocale. Pour plus d'informations, voir Paramètres régionaux.

Pour le code mixte et pure, _beginthread et _beginthreadex ont chacun deux surcharges. L'une accepte un pointeur de fonction à convention d'appel native, l'autre accepte un pointeur de fonction __clrcall. La première surcharge n'est pas sécurisée au niveau du domaine d'application et ne le sera jamais. Si vous écrivez du code mixte ou du code pure, vous devez vérifier que le nouveau thread accède au domaine d'application correct avant d'accéder à des ressources managées. Pour cela, vous pouvez par exemple utiliser fonction de call_in_appdomain. 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.

Configuration requise

Routine

En-tête requis

_beginthread

<process.h>

_beginthreadex

<process.h>

Pour plus d'informations sur la compatibilité, voir Compatibilité.

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.

Exemple

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 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 avec l'API de synchronisation WaitForSingleObject. Le thread principal attend que le second thread se termine avant de continuer. Lorsque le deuxième thread appelle _endthreadex, il fait passer son objet thread à l'état signalé. Le thread principal peut ainsi continuer à s'exécuter. Cela ne peut pas être fait avec _beginthread et _endthread, car _endthread appelle CloseHandle, qui détruit l'objet thread avant de pouvoir le définir avec 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 );
}
          

Équivalent .NET Framework

System::Threading::Thread::Start

Voir aussi

Référence

Contrôle de processus et d'environnement

_endthread, _endthreadex

abort

exit, _exit

GetExitCodeThread