_beginthread, _beginthreadex

Tworzy wątek.

Składnia

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
Rozpocznij adres procedury, która rozpoczyna wykonywanie nowego wątku. W przypadku _beginthreadmetody konwencja wywoływania to __cdecl (dla kodu natywnego) lub __clrcall (dla kodu zarządzanego). W przypadku _beginthreadexmetody konwencja wywoływania to __stdcall (dla kodu natywnego) lub __clrcall (dla kodu zarządzanego).

stack_size
Rozmiar stosu dla nowego wątku lub 0.

arglist
Lista argumentów, która ma zostać przekazana do nowego wątku lub NULL.

Security
Wskaźnik do SECURITY_ATTRIBUTES struktury, która określa, czy zwrócony uchwyt może być dziedziczony przez procesy podrzędne. Jeśli Security parametr ma NULLwartość , nie można dziedziczyć uchwytu.

initflag
Flagi kontrolujące stan początkowy nowego wątku. Ustaw initflag wartość 0, aby uruchomić natychmiast lub CREATE_SUSPENDED utworzyć wątek w stanie wstrzymania; użyj polecenia ResumeThread , aby wykonać wątek. Ustaw initflag wartość na STACK_SIZE_PARAM_IS_A_RESERVATION flagę, która ma być używana stack_size jako początkowy rozmiar rezerwowy stosu w bajtach. Jeśli ta flaga nie jest określona, stack_size określa rozmiar zatwierdzenia.

thrdaddr
Wskazuje zmienną 32-bitową, która otrzymuje identyfikator wątku. Jeśli jest NULLto wartość , nie jest używana.

Wartość zwracana

W przypadku powodzenia każda z tych funkcji zwraca dojście do nowo utworzonego wątku; jeśli jednak nowo utworzony wątek kończy się zbyt szybko, _beginthread może nie zwrócić prawidłowego uchwytu. (Zobacz dyskusję w sekcji Uwagi). W przypadku błędu _beginthread zwraca wartość -1L i errno jest ustawiona na EAGAIN wartość , jeśli istnieje zbyt wiele wątków, do EINVAL tego, czy argument jest nieprawidłowy, czy rozmiar stosu jest niepoprawny lub EACCES jeśli nie ma wystarczających zasobów (takich jak pamięć). W przypadku błędu _beginthreadex zwraca wartość 0 i errno_doserrno są ustawione.

Jeśli start_address parametr ma NULLwartość , wywoływana jest nieprawidłowa procedura obsługi parametrów, zgodnie z opisem w temacie Weryfikacja parametrów. Jeśli wykonanie może kontynuować, te funkcje są ustawione errno na EINVAL wartość i zwracają wartość -1.

Aby uzyskać więcej informacji na temat tych i innych kodów powrotnych, zobacz errno, _doserrno, _sys_errlisti _sys_nerr.

Aby uzyskać więcej informacji na temat uintptr_tprogramu , zobacz Typy standardowe.

Uwagi

Funkcja _beginthread tworzy wątek, który rozpoczyna wykonywanie procedury w .start_address W procedurze start_address należy użyć __cdecl (dla kodu natywnego) lub __clrcall (dla kodu zarządzanego) wywołującej konwencję i nie powinna mieć wartości zwracanej. Gdy wątek powróci z tej procedury, zostanie on automatycznie zakończony. Aby uzyskać więcej informacji na temat wątków, zobacz Obsługa wielowątków dla starszego kodu (Visual C++).

_beginthreadex bardziej przypomina interfejs API Win32 CreateThread , niż _beginthread to robi. _beginthreadex różni się od _beginthread następujących sposobów:

  • _beginthreadex Ma trzy więcej parametrów: initflag, Securityi threadaddr. Nowy wątek można utworzyć w stanie wstrzymania z określonymi zabezpieczeniami i można uzyskać do nich dostęp za pomocą elementu thrdaddr, który jest identyfikatorem wątku.

  • Procedury przekazywane w start_address celu _beginthreadex użycia __stdcall (dla kodu natywnego) lub __clrcall (dla kodu zarządzanego) wywołującej konwencję i muszą zwrócić kod zakończenia wątku.

  • _beginthreadex Zwraca wartość 0 w przypadku awarii, a nie -1L.

  • Wątek utworzony przy użyciu _beginthreadex jest przerywany przez wywołanie metody _endthreadex.

Funkcja _beginthreadex zapewnia większą kontrolę nad tym, jak jest tworzony wątek._beginthread Funkcja jest również bardziej elastyczna _endthreadex . Na przykład w programie _beginthreadexmożna użyć informacji zabezpieczających, ustawić początkowy stan wątku (uruchomiony lub zawieszony) i pobrać identyfikator wątku nowo utworzonego wątku. Możesz również użyć dojścia wątku zwróconego za pomocą _beginthreadex interfejsów API synchronizacji, których nie można wykonać za pomocą _beginthreadpolecenia .

_beginthreadex jest bezpieczniejszy do użycia niż _beginthread. Jeśli wątek, który jest generowany przez _beginthread wyjście szybko, uchwyt zwrócony do obiektu wywołującego _beginthread może być nieprawidłowy lub wskaż inny wątek. Jednak uchwyt zwrócony przez _beginthreadex element musi zostać zamknięty przez obiekt wywołujący _beginthreadexelementu , więc musi być prawidłowym uchwytem, jeśli _beginthreadex nie zwrócił błędu.

Można wywołać _endthread lub _endthreadex jawnie przerwać wątek; jednak lub _endthreadex jest wywoływany automatycznie, _endthread gdy wątek powraca z procedury przekazanej jako parametr. Przerywanie wątku za pomocą wywołania _endthread metody lub _endthreadex pomaga zapewnić poprawne odzyskiwanie zasobów przydzielonych dla wątku.

_endthread automatycznie zamyka uchwyt wątku, natomiast _endthreadex nie. W związku z tym w przypadku używania elementów _beginthread i _endthreadnie zamykaj jawnie dojścia wątku przez wywołanie interfejsu API Win32 CloseHandle . To zachowanie różni się od interfejsu API Win32 ExitThread .

Uwaga

W przypadku pliku wykonywalnego połączonego z biblioteką Libcmt.lib nie należy wywoływać interfejsu API Win32 ExitThread , aby nie uniemożliwić systemowi czasu wykonywania odzyskanie przydzielonych zasobów. _endthread i _endthreadex odzyskać przydzielone zasoby wątku, a następnie wywołać metodę ExitThread.

System operacyjny obsługuje alokację stosu, gdy _beginthread jest wywoływany lub _beginthreadex ; nie musisz przekazywać adresu stosu wątków do jednej z tych funkcji. Ponadto stack_size argument może mieć wartość 0, w takim przypadku system operacyjny używa tej samej wartości co stos określony dla wątku głównego.

arglist jest parametrem, który ma zostać przekazany do nowo utworzonego wątku. Zazwyczaj jest to adres elementu danych, taki jak ciąg znaków. arglist może być NULL taka, jeśli nie jest potrzebna, ale _beginthread_beginthreadex musi mieć pewną wartość do przekazania do nowego wątku. Wszystkie wątki są przerywane, jeśli jakikolwiek wątek wywołuje abort, exit, _exitlub ExitProcess.

Ustawienia regionalne nowego wątku są inicjowane przy użyciu globalnych informacji o bieżących ustawieniach regionalnych dla poszczególnych procesów. Jeśli ustawienia regionalne dla każdego wątku są włączone przez wywołanie metody _configthreadlocale (globalnie lub tylko dla nowych wątków), wątek może zmienić ustawienia regionalne niezależnie od innych wątków przez wywołanie setlocale lub _wsetlocale. Wątki, które nie mają ustawionej flagi ustawień regionalnych dla wątku, mogą mieć wpływ na informacje o ustawieniach regionalnych we wszystkich innych wątkach, które również nie mają ustawionej flagi ustawień regionalnych dla wątku, a także wszystkie nowo utworzone wątki. Aby uzyskać więcej informacji, zobacz Ustawienia regionalne.

W przypadku /clr kodu _beginthread każdy _beginthreadex z nich ma dwa przeciążenia. Jeden z nich przyjmuje natywny wskaźnik funkcji konwencji wywoływania, a drugi przyjmuje __clrcall wskaźnik funkcji. Pierwsze przeciążenie nie jest bezpieczne dla domeny aplikacji i nigdy nie będzie. Jeśli piszesz /clr kod, musisz upewnić się, że nowy wątek wchodzi w poprawną domenę aplikacji przed uzyskaniem dostępu do zasobów zarządzanych. Można to zrobić, na przykład za pomocą polecenia call_in_appdomain. Drugie przeciążenie jest bezpieczne dla domeny aplikacji; nowo utworzony wątek będzie zawsze znajdować się w domenie aplikacji obiektu wywołującego _beginthread elementu lub _beginthreadex.

Domyślnie stan globalny tej funkcji jest zakresem aplikacji. Aby zmienić to zachowanie, zobacz Stan globalny w CRT.

Wymagania

Procedura Wymagany nagłówek
_beginthread <process.h>
_beginthreadex <process.h>

Aby uzyskać więcej informacji o zgodności, zobacz Zgodność.

Biblioteki

Tylko wielowątkowe wersje bibliotek czasu wykonywania języka C.

Aby można było używać _beginthread programu lub _beginthreadex, aplikacja musi łączyć się z jedną z wielowątkowych bibliotek czasu wykonywania języka C.

Przykłady

W poniższym przykładzie użyto elementów _beginthread i _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();
}

Naciśnij dowolny klawisz, aby zakończyć przykładową aplikację.

Poniższy przykładowy kod pokazuje, jak można użyć dojścia wątku zwróconego za pomocą _beginthreadex interfejsu API WaitForSingleObjectsynchronizacji . Główny wątek czeka na zakończenie drugiego wątku przed kontynuowaniem. Gdy drugi wątek wywołuje _endthreadexelement , powoduje, że jego obiekt wątku przechodzi do stanu sygnalizowanego, co umożliwia kontynuowanie działania wątku podstawowego. Nie można go wykonać za pomocą _beginthread poleceń i _endthread, ponieważ _endthread wywołuje CloseHandlemetodę , która niszczy obiekt wątku, zanim będzie można ustawić go na stan zasygnalizowany.

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

Zobacz też