Share via


_beginthread, _beginthreadex

Crea un thread.

Sintassi

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

Parametri

start_address
Indirizzo iniziale di una routine che avvia l'esecuzione di un nuovo thread. Per _beginthread, la convenzione di __cdecl chiamata è (per il codice nativo) o __clrcall (per il codice gestito). Per _beginthreadex, la convenzione di __stdcall chiamata è (per il codice nativo) o __clrcall (per il codice gestito).

stack_size
Dimensione dello stack per un nuovo thread, oppure 0.

arglist
Elenco di argomenti da passare a un nuovo thread o NULL.

Security
Puntatore a una SECURITY_ATTRIBUTES struttura che determina se l'handle restituito può essere ereditato dai processi figlio. Se Security è NULL, l'handle non può essere ereditato.

initflag
Flag che controllano lo stato iniziale di un nuovo thread. Impostare initflag su 0 per l'esecuzione immediata o per CREATE_SUSPENDED creare il thread in uno stato sospeso. Usare ResumeThread per eseguire il thread. Impostare initflag su STACK_SIZE_PARAM_IS_A_RESERVATION come contrassegno da usare stack_size come dimensione di riserva iniziale dello stack in byte. Se questo flag non è specificato, stack_size specifica le dimensioni del commit.

thrdaddr
Punta a una variabile a 32 bit che riceve l'identificatore del thread. Se è NULL, non viene usato.

Valore restituito

Se completata correttamente, ognuna di queste funzioni restituisce un handle per il thread appena creato. Tuttavia, se il thread appena creato termina troppo rapidamente, _beginthread potrebbe non restituire un handle valido. Vedere la discussione nella sezione Osservazioni. In caso di errore, _beginthread restituisce -1L e errno viene impostato su EAGAIN se sono presenti troppi thread, su EINVAL se l'argomento non è valido o le dimensioni dello stack non sono corrette oppure su EACCES se sono presenti risorse insufficienti, ad esempio memoria. In caso di errore, _beginthreadex restituisce 0 e vengono impostati errno e _doserrno .

Se start_address è NULL, viene richiamato il gestore di parametri non validi, come descritto in Convalida dei parametri. Se l'esecuzione può continuare, queste funzioni impostano errno su EINVAL e restituiscono -1.

Per altre informazioni su questi e altri codici restituiti, vedere errno, _doserrno, _sys_errliste _sys_nerr.

Per altre informazioni su uintptr_t, vedere Tipi standard.

Osservazioni:

La funzione _beginthread crea un thread che inizia l'esecuzione di una routine presente in start_address. La routine in start_address deve usare la convenzione di chiamata __cdecl (per il codice nativo) o __clrcall (per il codice gestito) e non dovrebbe avere alcun valore restituito. Quando il thread viene restituito da tale routine, viene terminato automaticamente. Per altre informazioni sui thread, vedere Supporto multithreading per il codice precedente (Visual C++).

_beginthreadexassomiglia più strettamente _beginthread all'API Win32CreateThread. _beginthreadex differisce da _beginthread nei seguenti modi:

  • _beginthreadex ha altri tre parametri: initflag, Securitye threadaddr. Il nuovo thread può essere creato in uno stato sospeso, con una sicurezza specificata ed è possibile accedervi usando thrdaddr, che è l'identificatore del thread.

  • La routine in start_address che viene passata a _beginthreadex deve usare la convenzione di chiamata __stdcall (per il codice nativo) o __clrcall (per il codice gestito) e deve restituire un codice di uscita del thread.

  • _beginthreadex restituisce 0 in caso di errore, anziché -1L.

  • Un thread creato tramite _beginthreadex viene terminato da una chiamata a _endthreadex.

La funzione _beginthreadex offre maggiore controllo su come viene creato il thread rispetto a _beginthread . La funzione _endthreadex è inoltre più flessibile. Ad esempio, con _beginthreadex, è possibile usare informazioni di sicurezza, impostare lo stato iniziale del thread (in esecuzione o in sospeso) e ottenere l'identificatore del thread appena creato. È anche possibile usare l'handle di thread restituito da _beginthreadex con le API di sincronizzazione, che non è possibile eseguire con _beginthread.

_beginthreadex è più sicuro da usare rispetto _beginthreada . Se il thread generato da _beginthread termina rapidamente, l'handle restituito al chiamante di _beginthread potrebbe essere non valido o puntare a un altro thread. Tuttavia, l'handle restituito da _beginthreadex deve essere chiuso dal chiamante di _beginthreadex, quindi è garantito che sia un handle valido se _beginthreadex non ha restituito un errore.

È possibile chiamare o _endthreadex in modo _endthread esplicito per terminare un thread, _endthread ma viene _endthreadex chiamato automaticamente quando il thread viene restituito dalla routine passata come parametro. La terminazione di un thread con una chiamata a _endthread o a _endthreadex consente di assicurare il ripristino corretto delle risorse allocate per il thread.

_endthread chiude automaticamente l'handle di thread, mentre _endthreadex non lo è. Pertanto, quando si usa _beginthread e _endthread, non chiudere in modo esplicito l'handle di thread chiamando l'API Win32 CloseHandle . Questo comportamento è diverso dall'API Win32 ExitThread .

Nota

Per un file eseguibile collegato a Libcmt.lib, non chiamare l'API Win32 ExitThread in modo da non impedire al sistema di runtime di recuperare le risorse allocate. _endthread e _endthreadex recuperano le risorse del thread allocate, quindi chiamano ExitThread.

Il sistema operativo gestisce l'allocazione dello stack quando si chiama _beginthread o _beginthreadex. Non è necessario passare l'indirizzo dello stack di thread a una di queste funzioni. Inoltre, l'argomento stack_size può essere 0, nel qual caso il sistema operativo usa lo stesso valore dello stack specificato per il thread principale.

arglist è un parametro da passare al thread appena creato. In genere, si tratta dell'indirizzo di un elemento di dati, ad esempio una stringa di caratteri. arglist può essere NULL se non è necessario, ma _beginthread deve _beginthreadex essere assegnato un valore da passare al nuovo thread. Tutti i thread vengono terminati se un thread qualsiasi chiama abort, exit, _exito ExitProcess.

Le impostazioni locali del nuovo thread vengono inizializzate usando le informazioni locali correnti globali per processo. Se le impostazioni locali per thread sono abilitate da una chiamata a _configthreadlocale (solo a livello globale o solo per i nuovi thread), il thread può modificare le impostazioni locali indipendentemente da altri thread chiamando setlocale o _wsetlocale. I thread che non hanno il flag delle impostazioni locali per thread possono influire sulle informazioni sulle impostazioni locali in tutti gli altri thread che non hanno il flag delle impostazioni locali per thread e anche tutti i thread appena creati. Per altre informazioni, vedere Locale.

Per /clr il codice _beginthread e _beginthreadex ognuno ha due overload. Uno accetta un puntatore a una funzione di convenzione di chiamata nativa e l'altro accetta un __clrcall puntatore a funzione. Il primo overload non è sicuro per il dominio dell'applicazione e non sarà mai. Se si scrive /clr codice, è necessario assicurarsi che il nuovo thread entri nel dominio applicazione corretto prima di accedere alle risorse gestite. A tale scopo, ad esempio, è possibile usare call_in_appdomain. Il secondo overload è indipendente dal dominio applicazione. Il thread appena creato finirà sempre nel dominio applicazione del chiamante di _beginthread o di _beginthreadex.

Per impostazione predefinita, lo stato globale di questa funzione è limitato all'applicazione. Per modificare questo comportamento, vedere Stato globale in CRT.

Requisiti

Ciclo Intestazione obbligatoria
_beginthread <process.h>
_beginthreadex <process.h>

Per altre informazioni sulla compatibilità, vedere Compatibility (Compatibilità).

Librerie

Solo versioni multithread delle librerie di runtime C .

Per usare _beginthread o _beginthreadex, l'applicazione deve essere collegata a una delle librerie di runtime C con multithreading.

Esempi

Nell'esempio seguente vengono usate _beginthread e _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();
}

Premere un tasto qualsiasi per chiudere l'applicazione di esempio.

Il codice di esempio seguente illustra come usare l'handle di thread restituito da _beginthreadex con l'API WaitForSingleObjectdi sincronizzazione . Il thread principale attende che il secondo thread termini prima di continuare. Quando il secondo thread chiama _endthreadex, l'oggetto thread passa allo stato segnalato, che consente al thread primario di continuare l'esecuzione. Non può essere eseguita con _beginthread e _endthread, perché _endthread chiama CloseHandle, che elimina l'oggetto thread prima che possa essere impostato sullo stato segnalato.

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

Vedi anche