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?
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).
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.
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.
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.
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ć.
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()
.
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.
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;
}
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.
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:
- Określ atrybuty bezpieczeństwa.
- Uruchom wątek w stanie zawieszonym.
- możesz uzyskać identyfikator wątku, który może być używany z
OpenThread
. - zwrócony uchwyt wątku jest gwarantowany, jeśli wywołanie było
sukcesy Tam musisz zamknąć uchwyt za pomocą
CloseHandle
. - 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 nieCreateThread
orazExitThread
; wymaga to zastosowania wielowątkowej wersji CRT. Jeśli wątek created usingCreateThread
wywołuje CRT, CRT może zakończyć proces w warunkach niskiej pamięci.
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()
.
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.
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()
.
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.
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.
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
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
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