Sdílet prostřednictvím


_beginthread, _beginthreadex

Vytvoří vlákno.

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

Parametry

start_address
Zahajte adresu rutiny, která zahájí spuštění nového vlákna. Pro _beginthreadvolání je konvence buď __cdecl (pro nativní kód), nebo __clrcall (pro spravovaný kód). Pro _beginthreadexvolání je konvence buď __stdcall (pro nativní kód), nebo __clrcall (pro spravovaný kód).

stack_size
Velikost zásobníku pro nové vlákno nebo 0

arglist
Seznam argumentů, který se má předat novému vláknu, nebo NULL.

Security
Ukazatel na SECURITY_ATTRIBUTES strukturu, která určuje, zda vrácený popisovač lze dědit podřízenými procesy. Pokud Security ano NULL, popisovač nemůže být zděděný.

initflag
Označí příznaky, které řídí počáteční stav nového vlákna. Nastavte initflag na hodnotu 0, která se má spustit okamžitě, nebo vytvořte CREATE_SUSPENDED vlákno v pozastaveném stavu. Slouží ResumeThread ke spuštění vlákna. Nastavte initflag příznak, který STACK_SIZE_PARAM_IS_A_RESERVATION se má použít stack_size jako počáteční velikost zásobníku v bajtech. Pokud tento příznak není zadaný, stack_size určuje velikost potvrzení.

thrdaddr
Odkazuje na 32bitovou proměnnou, která přijímá identifikátor vlákna. Pokud je NULL, nepoužívá se.

Vrácená hodnota

V případě úspěchu vrátí každá z těchto funkcí popisovač nově vytvořenému vláknu; Pokud se však nově vytvořené vlákno příliš rychle ukončí, _beginthread nemusí vrátit platný popisovač. (Viz diskuze v části Poznámky.) Při chybě _beginthread vrátí hodnotu -1L a errno je nastavena na EAGAIN to, zda existuje příliš mnoho vláken, pokud EINVAL je argument neplatný nebo je nesprávná velikost zásobníku nebo EACCES pokud není k dispozici dostatek prostředků (například paměti). Při chybě _beginthreadex vrátí hodnotu 0 a errno_doserrno nastaví se.

Pokud start_address je NULL, je vyvolána neplatná obslužná rutina parametru, jak je popsáno v ověření parametru. Pokud je spuštění povoleno pokračovat, tyto funkce jsou nastavené errno na EINVAL hodnotu -1 a vrátí hodnotu -1.

Další informace o těchto a dalších návratových kódech naleznete v tématu , , , a_sys_nerr . _sys_errlist_doserrnoerrno

Další informace o uintptr_ttypu Standard naleznete v tématu Standardní typy.

Poznámky

Funkce _beginthread vytvoří vlákno, které zahájí provádění rutiny v start_address. Rutina start_address musí používat __cdecl konvenci volání (pro nativní kód) nebo __clrcall (pro spravovaný kód) a nesmí mít žádnou návratnou hodnotu. Když se vlákno vrátí z této rutiny, automaticky se ukončí. Další informace o vláknech najdete v tématu Podpora multithreadingu pro starší kód (Visual C++).

_beginthreadex podobá rozhraní API Win32 CreateThread blíže, než _beginthread tomu tak je. _beginthreadex se liší od _beginthread následujících způsobů:

  • _beginthreadex má tři další parametry: initflag, Securitya threadaddr. Nové vlákno lze vytvořit v pozastaveném stavu se zadaným zabezpečením a lze k němu přistupovat pomocí thrdaddridentifikátoru vlákna.

  • Rutina start_address , která _beginthreadex je předána, musí používat __stdcall konvenci volání (pro nativní kód) nebo __clrcall (pro spravovaný kód) a musí vrátit ukončovací kód vlákna.

  • _beginthreadex vrátí hodnotu 0 při selhání místo -1L.

  • Vlákno vytvořené pomocí je _beginthreadex ukončeno voláním _endthreadex.

Funkce _beginthreadex poskytuje větší kontrolu nad tím, jak se vlákno vytváří, než _beginthread dělá. Funkce _endthreadex je také flexibilnější. Můžete například _beginthreadexpoužít informace o zabezpečení, nastavit počáteční stav vlákna (spuštěno nebo pozastaveno) a získat identifikátor vlákna nově vytvořeného vlákna. Můžete také použít popisovač vlákna vrácený rozhraními _beginthreadex API pro synchronizaci, se kterými se nemůžete zabývat _beginthread.

_beginthreadex je bezpečnější používat než _beginthread. Pokud se vlákno vygenerované rychlým ukončením _beginthread ukončí, může být popisovač vrácený volajícímu _beginthread neplatný nebo odkazovat na jiné vlákno. Popisovač vrácený _beginthreadex volajícím _beginthreadexvšak musí být uzavřen , takže je zaručeno, že je platný popisovač, pokud _beginthreadex nevrátil chybu.

Vlákno můžete volat _endthread nebo _endthreadex explicitně ukončit, ale nebo _endthreadex se volá automaticky, _endthread když se vlákno vrátí z rutiny, která se předává jako parametr. Ukončení vlákna voláním _endthread nebo _endthreadex pomáhá zajistit správné obnovení prostředků přidělených pro vlákno.

_endthread automaticky zavře popisovač vlákna, zatímco _endthreadex ne. Proto při použití _beginthread a _endthread, nezavírejte explicitně popisovač vlákna voláním rozhraní API Win32 CloseHandle . Toto chování se liší od rozhraní API Win32 ExitThread .

Poznámka

U spustitelného souboru propojeného s knihovnou Libcmt.lib nevolejte rozhraní API win32 ExitThread , abyste nezabráněli systému za běhu v uvolnění přidělených prostředků. _endthread a _endthreadex uvolněte přidělené prostředky vlákna a pak volejte ExitThread.

Operační systém zpracovává přidělení zásobníku, když je _beginthread volána nebo _beginthreadex je volána. Do některé z těchto funkcí nemusíte předávat adresu zásobníku vláken. Kromě toho stack_size může být argument 0, v takovém případě operační systém používá stejnou hodnotu jako zásobník zadaný pro hlavní vlákno.

arglist je parametr, který se má předat nově vytvořenému vláknu. Obvykle se jedná o adresu datové položky, například řetězec znaků. arglist může být NULL v případě, že není potřeba, ale _beginthread_beginthreadex musí mít hodnotu, která se má předat novému vláknu. Všechna vlákna jsou ukončena, pokud jakékoli volání abortvlákna , exit, _exit, nebo ExitProcess.

Národní prostředí nového vlákna se inicializuje pomocí globálních informací o národním prostředí pro jednotlivé procesy. Pokud je národní prostředí pro jednotlivá vlákna povoleno voláním _configthreadlocale (globálně nebo pouze pro nová vlákna), může vlákno změnit své národní prostředí nezávisle na jiných vláknech voláním setlocale nebo _wsetlocale. Vlákna, která nemají nastavený příznak národního prostředí pro jednotlivá vlákna, můžou mít vliv na informace o národním prostředí ve všech ostatních vláknech, která také nemají nastavenou příznak národního prostředí pro jednotlivá vlákna, a také všechna nově vytvořená vlákna. Další informace naleznete v tématu Národní prostředí.

Pro /clr kód _beginthread a _beginthreadex každý z nich má dvě přetížení. Jeden přebírá nativní ukazatel funkce konvence volání a druhý přebírá __clrcall ukazatel funkce. První přetížení není bezpečné pro doménu aplikace a nikdy nebude. Pokud píšete /clr kód, musíte před přístupem ke spravovaným prostředkům zajistit, aby nové vlákno zadalo správnou doménu aplikace. Můžete to udělat například pomocí .call_in_appdomain Druhé přetížení je bezpečné pro doménu aplikace; nově vytvořené vlákno vždy skončí v doméně aplikace volajícího _beginthread nebo _beginthreadex.

Ve výchozím nastavení je globální stav této funkce vymezen na aplikaci. Chcete-li toto chování změnit, přečtěte si téma Globální stav v CRT.

Požadavky

Rutina Požadovaný hlavičkový soubor
_beginthread <process.h>
_beginthreadex <process.h>

Další informace o kompatibilitě najdete v tématu Kompatibilita.

Knihovny

Pouze vícevláknové verze knihoven runtime jazyka C.

Chcete-li použít _beginthread nebo _beginthreadex, musí aplikace propojit s jednou z vícevláknových knihoven jazyka C za běhu.

Příklady

Následující příklad používá _beginthread a _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();
}

Stisknutím libovolné klávesy ukončete ukázkovou aplikaci.

Následující ukázkový kód ukazuje, jak můžete použít popisovač vlákna vrácený rozhraním _beginthreadex API WaitForSingleObjectpro synchronizaci . Hlavní vlákno čeká na ukončení druhého vlákna, než bude pokračovat. Když druhé vlákno volá _endthreadex, způsobí, že jeho vlákno objekt přejde do signalovaného stavu, což umožňuje primární vlákno pokračovat ve spuštění. Nelze to provést s _beginthread a _endthread, protože _endthread volání CloseHandle, která zničí vlákno objekt předtím, než lze nastavit na signalovaný stav.

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

Viz také