Windows threading: beginthread vs beginthreadex vs CreateThread C++

Jaki jest lepszy sposób na rozpoczęcie wątku, _beginthread, _beginthreadx albo CreateThread?

Staram się ustalić jakie są zalety/wady _beginthread, _beginthreadex i CreateThread. Wszystkie te funkcje zwracają uchwyt wątku do nowo utworzonego wątku, wiem już, że CreateThread dostarcza trochę dodatkowych informacji, gdy wystąpi błąd(można to sprawdzić wywołując GetLastError)... ale jakie są pewne rzeczy, które powinienem wziąć pod uwagę, gdy używam tych funkcji?

Pracuję z aplikacja windows, więc kompatybilność między platformami jest już wykluczona.

Przejrzałem dokumentację msdn i po prostu nie mogę zrozumieć, na przykład, dlaczego ktoś zdecydował się użyć _beginthread zamiast CreateThread lub odwrotnie.

Zdrówko!

Update: OK, dzięki za wszystkie info, czytałem też w kilku miejscach, że nie mogę zadzwonić WaitForSingleObject() jeśli użyłem _beginthread(), ale jeśli zadzwonię _endthread() w wątku nie powinno to działać? O co chodzi? tam?

Author: MD XF, 2008-12-01

17 answers

CreateThread() jest surowym wywołaniem API Win32 do utworzenia kolejnego wątku kontroli na poziomie jądra.

_beginthread() & _beginthreadex() są wywołaniami biblioteki uruchomieniowej C, które wywołują CreateThread() Za kulisami. Po powrocie CreateThread(), _beginthread/ex() zajmuje się dodatkową księgowością, aby Biblioteka uruchomieniowa C była użyteczna i spójna w nowym wątku.

W C++ powinieneś prawie na pewno używać _beginthreadex(), chyba że w ogóle nie będziesz linkował do biblioteki uruchomieniowej C (aka MSVCRT*.dll/.lib).

 97
Author: Drew Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-23 21:01:27

Istnieje kilka różnic między _beginthread() i _beginthreadex(). _beginthreadex() został stworzony, aby działać bardziej jak CreateThread() (zarówno w parametrach, jak i w jaki sposób się zachowuje).

Jak wspomina Drew Hall , jeśli używasz środowiska wykonawczego C / C++, musisz użyć _beginthread()/_beginthreadex() zamiast CreateThread(), Aby runtime miał szansę wykonać własną inicjalizację wątku (ustawienie lokalnego magazynu wątków, itd.).

W praktyce oznacza to, że CreateThread() w zasadzie nigdy nie powinny być używane bezpośrednio przez kod.

Dokumenty MSDN dla _beginthread()/_beginthreadex() mają sporo szczegółów na temat różnic - jednym z ważniejszych jest to, że ponieważ uchwyt wątku dla wątku utworzonego przez _beginthread() jest automatycznie zamykany przez CRT, gdy wątek się kończy, "jeśli wątek wygenerowany przez _beginthread szybko się kończy, uchwyt zwrócony do wywołującego _beginthread może być nieprawidłowy lub, co gorsza, wskazywać na inny wątek".

Oto co komentarze do _beginthreadex() w źródle CRT muszą powiedz:

Differences between _beginthread/_endthread and the "ex" versions:

1)  _beginthreadex takes the 3 extra parameters to CreateThread
  which are lacking in _beginthread():
    A) security descriptor for the new thread
    B) initial thread state (running/asleep)
    C) pointer to return ID of newly created thread

2)  The routine passed to _beginthread() must be __cdecl and has
  no return code, but the routine passed to _beginthreadex()
  must be __stdcall and returns a thread exit code.  _endthread
  likewise takes no parameter and calls ExitThread() with a
  parameter of zero, but _endthreadex() takes a parameter as
  thread exit code.

3)  _endthread implicitly closes the handle to the thread, but
  _endthreadex does not!

4)  _beginthread returns -1 for failure, _beginthreadex returns
  0 for failure (just like CreateThread).

Aktualizacja Styczeń 2013:

CRT dla VS 2012 ma dodatkowy bit inicjalizacji wykonywany w _beginthreadex(): jeśli Proces jest "spakowaną aplikacją" (jeśli coś użytecznego zostanie zwrócone z GetCurrentPackageId()), runtime zainicjalizuje MTA w nowo utworzonym wątku.

 38
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-15 13:25:56

Ogólnie rzecz biorąc, poprawne jest wywołanie _beginthread()/_endthread() (LUB ex() wariantów). Jednak, jeśli używasz CRT jako.dll, stan CRT zostanie prawidłowo zainicjowany i zniszczony, ponieważ DllMain CRT zostanie wywołany z DLL_THREAD_ATTACH i DLL_THREAD_DETACH podczas wywołania CreateThread() i ExitThread() lub powrotu, odpowiednio.

Kod DllMain dla CRT można znaleźć w katalogu instalacyjnym VS pod VC \ crt \ src \ crtlib.c.

 24
Author: MSN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-19 00:01:15

To jest KOD u podstaw _beginthreadex (Zobacz crt\src\threadex.c):

    /*
     * Create the new thread using the parameters supplied by the caller.
     */
    if ( (thdl = (uintptr_t)
          CreateThread( (LPSECURITY_ATTRIBUTES)security,
                        stacksize,
                        _threadstartex,
                        (LPVOID)ptd,
                        createflag,
                        (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
    {
            err = GetLastError();
            goto error_return;
    }

Reszta _beginthreadex inicjalizuje strukturę danych dla każdego wątku dla CRT.

Zaletą używania _beginthread* jest to, że Twoje wywołania CRT z wątku będą działać poprawnie.

 17
Author: Constantin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-01 18:33:16

Powinieneś użyć _beginthread LUB _beginthreadex, aby umożliwić bibliotece uruchomieniowej C samodzielną inicjalizację wątku. Tylko programiści C / C++ muszą to znać, ponieważ powinni teraz znać zasady korzystania z własnego środowiska programistycznego.

Jeśli używasz _beginthread nie musisz wywoływać CloseHandle, Jak zrobi to RTL za Ciebie. Dlatego nie możesz czekać na uchwyt, jeśli użyłeś _beginthread. Również _beginthread prowadzi do zamieszania, jeśli funkcja wątku wychodzi natychmiast (szybko) jako wątek uruchamiający my be w lewo trzymając nieprawidłowy uchwyt wątku do wątku, który właśnie uruchomił.

_beginthreadex uchwyty mogą być używane do wait, ale również wymagają jawnego wywołania CloseHandle. Jest to część tego, co sprawia, że są bezpieczne do użycia z wait. Innym problemem, aby był całkowicie niezawodny, jest zawsze uruchamianie zawieszonego wątku. Sprawdź sukces, uchwyt rekordu itp. Wątek CV. Jest to wymagane, aby zapobiec zakończeniu wątku, zanim wątek startowy będzie mógł nagrać swój uchwyt.

Best praktyka polega na użyciu _beginthreadex, start zawieszony następnie wznowić po nagraniu uchwyt, czekać na uchwyt jest OK, {[3] } należy wywołać.

 13
Author: jarcher7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:46:48

CreateThread() używane do wycieków pamięci podczas korzystania z funkcji CRT w kodzie. _beginthreadex() ma te same parametry co CreateThread() i jest bardziej uniwersalny niż _beginthread(). Dlatego polecam użycie _beginthreadex().

 8
Author: Jaywalker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:57:54

Odnośnie Twojego zaktualizowanego pytania: "czytałem też w kilku miejscach, których nie mogę wywołać WaitForSingleObject(), jeśli użyłem _beginthread(), ale jeśli wywołam _endthread() w wątku, Czy to nie powinno działać?"

Ogólnie można przekazać uchwyt wątku do WaitForSingleObject() (lub innych interfejsów API, które oczekują na uchwyty obiektów), aby zablokować do zakończenia wątku. Ale uchwyt wątku utworzony przez _beginthread() jest zamykany, gdy wywołane jest _endthread() (co może być wykonane jawnie lub jest wykonywane domyślnie przez czas wykonania, gdy procedura wątku zwroty).

Problem jest wywoływany w dokumentacji dla WaitForSingleObject():

Jeśli ten uchwyt jest zamknięty podczas oczekiwania, zachowanie funkcji jest niezdefiniowane.

 6
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-01 19:35:30

Patrząc na sygnatury funkcji, CreateThread jest prawie identyczna z _beginthreadex.

_beginthread, _beginthreadx vs CreateThread

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

uintptr_t _beginthread( 
   void( *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

Uwagi na temat tutaj mówią _beginthread mogą używać __cdecl lub __clrcall jako punktu początkowego, a _beginthreadex mogą używać __stdcall lub __clrcall jako punktu początkowego.

Myślę, że wszelkie komentarze ludzi na temat wycieków pamięci w CreateThread są ponad dziesięcioletnie i powinny być prawdopodobnie ignorowane.

Co ciekawe, obie _beginthread* funkcje właściwie zadzwoń pod maskę, w C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src na mojej maszynie.

// From ~line 180 of beginthreadex.c
/*
 * Create the new thread using the parameters supplied by the caller.
 */
if ( (thdl = (uintptr_t)
      CreateThread( (LPSECURITY_ATTRIBUTES)security,
                    stacksize,
                    _threadstartex,
                    (LPVOID)ptd,
                    createflag,
                    (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
{
        err = GetLastError();
        goto error_return;
}
 5
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:57:10

beginthreadex daje Ci wątek {[1] } do użytku w WaitForSingleObject i znajomych. Nie zapomnij, kiedy skończysz. Prawdziwą odpowiedzią byłoby użycie boost::thread lub wkrótce klasy thread C++09.

 3
Author: MD XF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:50:17

W porównaniu do _beginthread, z _beginthreadex możesz:

  1. Określ atrybuty bezpieczeństwa.
  2. Uruchom wątek w stanie zawieszonym.
  3. możesz uzyskać identyfikator wątku, który może być używany z OpenThread.
  4. zwrócony uchwyt wątku jest gwarantowany, jeśli wywołanie było sukcesy Tam musisz zamknąć uchwyt za pomocą CloseHandle.
  5. zwrócony uchwyt wątku może być używany z interfejsami API synchronizacji.

_beginthreadex bardzo przypomina CreateThread, ale pierwsza jest implementacją CRT, a druga wywołaniem Windows API. Dokumentacja dla CreateThread zawiera następujące zalecenie:

Wątek w pliku wykonywalnym, który wywołuje bibliotekę C run-time (CRT) powinien używać _beginthreadex oraz _endthreadex funkcje do zarządzania wątkami, a nie CreateThread oraz ExitThread; wymaga to zastosowania wielowątkowej wersji CRT. Jeśli wątek created using CreateThread wywołuje CRT, CRT może zakończyć proces w warunkach niskiej pamięci.

 2
Author: Vishal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:51:32

CreateThread() kiedyś było Nie-Nie, ponieważ CRT byłoby nieprawidłowo zainicjować / oczyścić. Ale to jest teraz Historia: można teraz (używając VS2010 i prawdopodobnie kilka wersji wstecz) wywołać CreateThread() bez łamania CRT.

Oto oficjalne potwierdzenie MS . Stwierdza jeden wyjątek:

Właściwie jedyną funkcją, która nie powinna być używana w wątku utworzony za pomocą CreateThread() Jest funkcją signal().

Jednakże, z punktu widzenia konsystencji, ja osobiście wolę używać _beginthreadex().

 2
Author: Serge Wautier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:52:14

CreateThread() czy wywołanie Windows API jest neutralne językowo. Po prostu tworzy OS object-thread i zwraca uchwyt do tego wątku. Wszystkie aplikacje windows używają tego wywołania do tworzenia wątków. Wszystkie języki unikają bezpośredniego wywołania API z oczywistych powodów: 1. Nie chcesz, aby Twój kod był specyficzny dla systemu operacyjnego 2. Przed wywołaniem API musisz wykonać kilka czynności domowych,takich jak: konwersja parametrów i wyników, przydzielanie tymczasowego magazynu itp.

_beginthreadex() jest wrapperem C wokół CreateThread(), który odpowiada C specyficznemu. Informatyka umożliwia oryginalną pracę z pojedynczym gwintem C f-ns w środowisku wielowątkowym poprzez przydzielanie pamięci specyficznej dla wątku.

Jeśli nie używasz CRT, nie możesz uniknąć bezpośredniego połączenia z CreateThread(). Jeśli używasz CRT, musisz użyć _beginthreadex() lub jakiś łańcuch CRT f-ns może nie działać poprawnie przed VC2005.

 2
Author: SKV,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:52:51

CreateThread() to proste połączenie systemowe. Jest zaimplementowany na Kernel32.dll, z którym najprawdopodobniej Twoja aplikacja będzie już powiązana z innymi przyczynami. Jest zawsze dostępny w nowoczesnych systemach Windows.

_beginthread() i {[3] } są funkcjami wrappera w Microsoft C Runtime (msvcrt.dll). Różnice między tymi dwoma wywołaniami są podane w dokumentacji. Jest on więc dostępny, gdy Microsoft C Runtime jest dostępny lub jeśli aplikacja jest powiązana statycznie z nią. Będziesz prawdopodobnie też linkujesz z tą biblioteką, chyba że kodujesz w czystym Windows API (jak ja osobiście często to robię).

Twoje pytanie jest spójne i właściwie powtarzające się. Jak wiele interfejsów API, mamy do czynienia z zduplikowanymi i niejednoznacznymi funkcjami w interfejsie API systemu Windows. Co najgorsze, dokumentacja nie wyjaśnia problemu. Przypuszczam, że rodzina funkcji _beginthread() została stworzona dla lepszej integracji z innymi standardowymi funkcjami C, takimi jak manipulacja errno. _beginthread() w ten sposób lepiej integruje się z uruchomieniem C.

Pomimo tego, o ile nie masz dobrych powodów do używania _beginthread() lub _beginthreadex(), powinieneś używać CreateThread(), głównie dlatego, że możesz uzyskać o jedną zależność bibliotek mniej w swoim finalnym pliku wykonywalnym(a dla MS CRT to ma znaczenie). Nie ma również zawijania kodu wokół połączenia, chociaż efekt ten jest znikomy. Innymi słowy, uważam, że głównym powodem trzymania się CreateThread() jest to, że nie ma dobrego powodu, aby używać _beginthreadex() na początek. Funkcjonalności są dokładnie lub prawie takie same.

Jeden dobry powód, aby używać _beginthread() Jeśli wywołane zostanie , obiekty C++ będą prawidłowo rozwijane/niszczone, jeśli wywołane zostanie _endthread().

 1
Author: alecov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-18 12:51:02

Inne odpowiedzi nie omawiają implikacji wywołania funkcji C run-time, która zawija funkcję API Win32. Jest to ważne przy rozważaniu zachowania blokowania loadera DLL.

Niezależnie od tego, czy _beginthread{ex} wykonuje jakieś specjalne zarządzanie pamięcią C Runtime thread/fiber, o czym dyskutują inne odpowiedzi, jest ono zaimplementowane w (zakładając dynamiczne powiązanie z czasem wykonywania C) DLL, którego procesy mogą jeszcze nie załadować.

Nie jest bezpiecznie dzwonić _beginthread* z DllMain. Przetestowałem to przez zapisanie biblioteki DLL załadowanej za pomocą funkcji Windows "AppInit_DLLs". Wywołanie _beginthreadex (...) zamiast CreateThread (...) powoduje, że wiele ważnych części systemu Windows przestaje działać podczas rozruchu, ponieważ punkty wejścia DllMain oczekują na zwolnienie blokady loader w celu wykonania określonych zadań inicjalizacji.

Nawiasem mówiąc, właśnie dlatego kernel32.dll ma wiele nakładających się funkcji łańcuchowych, które również wykonuje C-time -- użyj tych z DllMain, aby uniknąć tego samego rodzaju sytuacja.

 0
Author: Andon M. Coleman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-16 07:15:47

Jeśli czytasz książkę debugowanie aplikacji Windows od Jeffrey Richter w niej wyjaśnia, że prawie we wszystkich przypadkach należy wywołać _beginthreadex zamiast wywoływać CreateThread. _beginthread jest tylko uproszczoną owijką wokół _beginthreadex.

_beginthreadex inicjalizuje pewne wewnętrzne środowiska uruchomieniowe CRT (C RunTime), których API CreateThread by nie wykonało.

Konsekwencją użycia API CreateThread zamiast wywołań funkcji CRT może być nieoczekiwane spowodowanie problemów.

Sprawdź ten stary Microsoft Journal Od Richtera.

 0
Author: Ehsan Samani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-09 02:53:59

Powinieneś wypróbować ten kod

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<process.h>

UINT __stdcall Staff(PVOID lp){
 printf("The Number is %d\n", GetCurrentThreadId());
 return 0;
}

INT main(INT argc, PCHAR argv[])
{

    const INT Staff_Number = 5;
    HANDLE hd[Staff_Number];
    for(INT i=0; i < Staff_Number; i++){
       hd[i] = (HANDLE)_beginthreadex(NULL, 0, Staff, NULL, 0, NULL);
    }

 WaitForMultipleObjects(Staff_Number, Staff, TRUE, NULL);
 for(INT i=0; i < Staff_Number; i++)
 {
     CloseHandle(hd[i]);
 }
 system("pause");
 return 0;
}

Jeśli używasz _beginthread zamiast _beginthreadex to da błąd zbyt wiele argumentów dla _beginthread dzieje się tak dlatego, że _beginthread nie mógł utworzyć wątku z atrybutem bezpieczeństwa a także uważam, że _beginthread jest zbędny możesz bezwzględnie użyć *(_beginthreadex) i CreateThread

 -1
Author: testhaha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-08 09:50:47

Nie ma już różnicy między nimi.

Wszystkie komentarze dotyczące wycieków pamięci itp. są oparte na bardzo starych wersjach

 -2
Author: Lothar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-04 04:42:31