Wątki w systemie Windows: _beginthread vs _beginthreadex vs CreateThread C ++


134

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

Próbuję ustalić, jakie są zalety / wady _beginthread, _beginthreadexi CreateThread. Wszystkie te funkcje zwracają uchwyt wątku do nowo utworzonego wątku, już wiem, że CreateThread dostarcza trochę dodatkowych informacji, gdy wystąpi błąd (można to sprawdzić, wywołując GetLastError) ... ale jakie są rzeczy, które powinienem rozważyć, kiedy ja ' korzystam z tych funkcji?

Pracuję z aplikacją Windows, więc kompatybilność między platformami jest już wykluczona.

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

Twoje zdrowie!

Aktualizacja: OK, dzięki za wszystkie informacje, przeczytałem też w kilku miejscach, do których nie mogę zadzwonić, WaitForSingleObject()gdybym korzystał _beginthread(), ale jeśli dzwonię _endthread()w wątku, czy to nie powinno działać? O co tam chodzi?


2
Oto analiza tego, co _beginthreadex () robi dla programistów C / C ++ , którą znalazłem z linku na stronie Eli Bendersky'ego. To pochodzi z pytań i odpowiedzi dotyczących tego, czy używać funkcji CreateThread (), czy nie. microsoft.com/msj/0799/win32/win320799.aspx
Richard Chambers

Odpowiedzi:


97

CreateThread() to surowe wywołanie API Win32 do tworzenia kolejnego wątku kontroli na poziomie jądra.

_beginthread()& _beginthreadex()to wywołania biblioteki wykonawczej C, które wywołują CreateThread()za kulisami. Po CreateThread()powrocie _beginthread/ex()zajmuje się dodatkową księgowością, aby biblioteka środowiska wykonawczego C była użyteczna i spójna w nowym wątku.

W C ++ prawie na pewno powinieneś używać, _beginthreadex()chyba że w ogóle nie będziesz łączyć się z biblioteką wykonawczą C (aka MSVCRT * .dll / .lib).


40
Nie jest to już tak prawdziwe, jak kiedyś. CRT będzie działać poprawnie w wątku utworzonym przez CreateThread () z wyjątkiem funkcji signal (). Wystąpi mały wyciek pamięci (~ 80 bajtów) dla każdego wątku utworzonego za pomocą CreateThread (), który używa CRT, ale będzie działać poprawnie. Aby uzyskać więcej informacji, zobacz: support.microsoft.com/default.aspx/kb/104641
John Dibling

1
@John: Właściwie ten błąd dotyczy tylko MSVC ++ 6.0
bobobobo

5
@bobobobo: Dobre pytanie. Mogę tylko spekulować, że pierwotnie MS zamierzało _beginwywołać procedury wewnętrzne, a CreateThreadmiało być funkcją API, którą wszyscy będą wywoływać. Innym potencjalnym wyjaśnieniem jest to, że stwardnienie rozsiane ma długą i wspaniałą historię ignorowania standardów i podejmowania bardzo złych decyzji dotyczących nazewnictwa rzeczy.
John Dibling

15
Te _beginfunkcje zaczynają się od podkreślenia , ponieważ Microsoft zaczął zgodne ze standardem bliżej. W środowisku wykonawczym C nazwy z podkreśleniem są zarezerwowane dla implementacji (a implementacja może je udokumentować do użytku przez użytkownika końcowego, tak jak w przypadku tych). beginthreadex()to nazwa, której może używać użytkownik. Gdyby środowisko wykonawcze C go użyło, mogłoby to powodować konflikt z symbolem użytkownika końcowego, którego użytkownik miał uzasadnione prawo, aby móc go używać. Należy zauważyć, że interfejsy API Win32 nie są częścią środowiska wykonawczego C i używają przestrzeni nazw użytkownika.
Michael Burr

2
@Lothar: Tam różnice między wywołania API Win32 CreateThreadi połączeniami CRT _beginthread/ex, a podczas wywoływania CRT na wątku, to zawsze powinny być tworzone z _beginthread/ex. Jeśli tego nie zrobisz, może już nie być wycieków pamięci. Ale z pewnością nie otrzymasz poprawnie zainicjalizowanego środowiska zmiennoprzecinkowego CreateThread, na przykład podczas wywoływania . Jest więcej : „Jeśli wątek utworzony za pomocą CreateThread wywołuje CRT, CRT może zakończyć proces w warunkach małej ilości pamięci”.
Niespodziewane

38

Istnieje kilka różnic między _beginthread()i _beginthreadex(). _beginthreadex()został stworzony, aby zachowywać się bardziej podobnie CreateThread()(zarówno pod względem parametrów, jak i zachowania).

Jak wspomina Drew Hall , jeśli używasz środowiska uruchomieniowego C / C ++, musisz użyć _beginthread()/ _beginthreadex()zamiast, CreateThread()aby środowisko wykonawcze miało szansę na wykonanie własnej inicjalizacji wątku (ustawienie lokalnego magazynu wątku itp.).

W praktyce oznacza to, że CreateThread()prawie nigdy nie powinno być używane bezpośrednio w kodzie.

Dokumenty MSDN dla _beginthread()/ _beginthreadex()zawierają 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 po zakończeniu wątku, „jeśli wątek wygenerowany przez _beginthread kończy działanie szybko uchwyt zwrócony do obiektu wywołującego _beginthread może być nieprawidłowy lub, co gorsza, wskazywać na inny wątek ".

Oto, co _beginthreadex()mają do powiedzenia komentarze w źródle CRT:

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 zwracane jest coś użytecznego GetCurrentPackageId()), środowisko wykonawcze zainicjuje MTA w nowo utworzonym wątku.


3
Tam odpowiednie czasy kiedy CreateThread () jest uzasadnione, ale szczerze mówiąc to naprawdę trzeba iść z sposób, aby to zrobić. Mówimy o całkowitym braku czegokolwiek przenośnego i piszemy wyłącznie bibliotekę DLL lub aplikację WIN32 API. W tym brak wywołań C-runtime. Nawet użycie STL jest ograniczone, ponieważ musisz zapewnić niestandardowe alokatory, aby używać funkcji zarządzania pamięcią WIN32. Konfiguracja do tego w Developer Studio jest zadaniem samym w sobie, ale dla jedynej zawsze biblioteki WIN32 o najmniejszej możliwej powierzchni można to zrobić. Ale tak, to nie jest cholernie prawdopodobne dla prawie wszystkich, ale bardzo wybranych.
WhozCraig

1
@WhozCraig: Pomijanie CRT wiąże się z poważniejszymi ograniczeniami. Najważniejsze z nich to: brak obsługi 64-bitowych liczb całkowitych, brak obsługi zmiennoprzecinkowej i - najbardziej drastycznie - brak obsługi wyjątków. To naprawdę oznacza brak obsługi wyjątków - w ogóle . Nawet wyjątków SEH. Jest to szczególnie trudne do nadrobienia, a szanse na to, CreateThreadże zadzwonisz do właściwej osoby, są coraz mniejsze.
Niespodziewane


@Mehrdad: O jakich konkretnie zmianach warto wspomnieć?
Inspectable

Zauważyłem, że DisableThreadLibraryCalls nie ma wpływu na wątki utworzone za pomocą CreateThread, ale wyłącza wątki utworzone za pomocą _beginthread lub _beginthreadex.
SPlatten

24

Ogólnie rzecz biorąc, właściwą rzeczą do zrobienia jest wywołanie _beginthread()/_endthread()(lub ex()warianty). Jeśli jednak użyjesz CRT jako pliku dll, stan CRT zostanie poprawnie zainicjowany i zniszczony, gdy CRT DllMainzostanie wywołany odpowiednio z DLL_THREAD_ATTACHi DLL_THREAD_DETACHpodczas wywoływania CreateThread()i ExitThread()lub powrotu.

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


Świetna baza wypadowa. Przy odrobinie debugowania można pokazać, że __CRTDLL_INIT jest wywoływana nawet dla statycznie połączonego CRT. Stos wywołań init jest wywoływany z _LdrpCallInitRoutine @ 16 (), nie jestem pewien, jaki dokładnie mechanizm. Oznacza to, że w ostatnim CRT cała inicjalizacja / deinicjalizacja jest wykonywana poprawnie, z wyjątkiem obsługi sygnału, która jest nadal wykonywana w funkcji pomocniczej _threadstartex wywoływanej z beginthread, ale nie z CreateThread. Może mógłbyś dodać to do odpowiedzi, a ja przyznam nagrodę?
Suma

Nagroda przyznana, ponieważ wydaje się to najbardziej pomocne. Mimo to odpowiedź może być warta aktualizacji. Jeśli nie możesz tego zrobić, mogę wrócić do tego w ciągu kilku dni.
Suma,

1
@MSN: Należy pamiętać, że funkcja CreateThread nadal jest zła w bibliotece DLL, jeśli łączysz się ze statycznym CRT i wywołałeś DisableThreadLibraryCalls, co wyłącza wywołania DLL_THREAD_DETACH. Wtedy będziesz miał wycieki pamięci. Jest to udokumentowane w moim artykule KB: support.microsoft.com/kb/555563/en-us
Jochen Kalmbach

17

To jest kod leżący 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 _beginthreadexinicjuje strukturę danych na wątek dla CRT.

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


13

Powinieneś użyć _beginthreadlub, _beginthreadexaby pozwolić bibliotece wykonawczej C na wykonanie własnej inicjalizacji wątku. Tylko programiści C / C ++ muszą to wiedzieć, ponieważ powinni teraz znać zasady korzystania z własnego środowiska programistycznego.

Jeśli używasz _beginthread, nie musisz dzwonić, CloseHandleponieważ RTL zrobi to za Ciebie. Dlatego nie możesz czekać na uchwycie, jeśli użyłeś _beginthread. _beginthreadProwadzi również do zamieszania, jeśli funkcja wątku kończy pracę natychmiast (szybko), ponieważ wątek uruchamiający może pozostać z nieprawidłowym uchwytem wątku do wątku, który właśnie uruchomił.

_beginthreadexuchwyty mogą służyć do oczekiwania, ale wymagają również jawnego wywołania CloseHandle. Jest to część tego, co sprawia, że ​​można ich bezpiecznie używać z czekaniem. Innym problemem, który sprawia, że ​​jest całkowicie niezawodny, jest zawsze rozpoczynanie zawieszonego wątku. Sprawdź powodzenie, uchwyt rekordu itp. Wątek wznowienia. Jest to wymagane, aby zapobiec zakończeniu wątku, zanim wątek uruchamiający będzie mógł zarejestrować swój uchwyt.

Najlepszą praktyką jest użycie _beginthreadex, rozpoczęcie zawieszenia, a następnie wznowienie po nagraniu uchwytu, oczekiwanie na uchwyt jest OK, CloseHandlemusi zostać wywołane.


8

CreateThread()kiedyś występowały przecieki pamięci podczas używania funkcji CRT w kodzie. _beginthreadex()ma takie same parametry jak CreateThread()i jest bardziej wszechstronny niż _beginthread(). Więc polecam ci użycie _beginthreadex().


2
Artykuł z 1999 roku, mógł zostać naprawiony
bobobobo

1
Ten artykuł z 2005 roku nadal potwierdza, że ​​istnieje problem.
Jaywalker

2
Tak, dotyczy tylko MSVC ++ 6.0 Service Pack 5 i wcześniejszych. (zobacz rozwijane menu „Dotyczy”). Nie stanowi to dziś problemu, jeśli używasz VC7 lub nowszego.
bobobobo

1
Jest to nadal problem, jeśli łączysz się ze statycznym CRT! Nadal występuje problem, jeśli wywołasz DisableThreadLibraryCalls w bibliotece DLL, która jest połączona statycznie; zobacz mój artykuł z
bazy wiedzy

2
Państwo nieprawdziwe informacje: CreateThreadczy nie kiedykolwiek wyciek pamięci. To raczej CRT robi to, gdy jest wywoływany z wątku, który nie został poprawnie zainicjowany.
Niespodziewane

6

Odnosząc się do twojego zaktualizowanego pytania: „Czytałem też w kilku miejscach, do których nie mogę zadzwonić, WaitForSingleObject()jeśli użyłem _beginthread(), ale jeśli dzwonię _endthread()w wątku, czy to nie powinno działać?”

Ogólnie rzecz biorąc, można przekazać uchwyt wątku do WaitForSingleObject()(lub inne interfejsy API, które czekają na uchwyty obiektów), aby blokować do momentu zakończenia wątku. Ale uchwyt wątku utworzony przez _beginthread()jest zamykany, gdy _endthread()jest wywoływany (co można zrobić jawnie lub niejawnie przez czas wykonywania, gdy procedura wątku zwraca).

Problem pojawia się w dokumentacji WaitForSingleObject():

Jeśli ten uchwyt zostanie zamknięty, gdy oczekiwanie jest nadal w toku, zachowanie funkcji jest niezdefiniowane.


5

Patrząc na sygnatury funkcji, CreateThreadjest prawie identyczny z _beginthreadex.

_beginthread,_beginthreadx vsCreateThread

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ą , że _beginthreadmożna użyć konwencji wywoływania albo __cdecllub __clrcalljako punktu początkowego, a jako punktu początkowego _beginthreadexmożna użyć albo __stdcallalbo __clrcall.

Myślę, że wszelkie komentarze ludzi na temat wycieków pamięci CreateThreadmają ponad dekadę i prawdopodobnie powinny zostać zignorowane.

Co ciekawe, obie _beginthread*funkcje faktycznie wywołują CreateThreadpod maską, w C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\srcmoim komputerze.

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

2
Skomentuj, dlaczego nie powinieneś wywoływać CreateThread i mieszać wywołań CRT w tym wątku (zdecydowanie nie sprzed dziesięciu lat i zdecydowanie nie należy go ignorować) : „Jeśli wątek utworzony za pomocą CreateThread wywołuje CRT, CRT może zakończyć proces w warunki małej ilości pamięci ”.
Niespodziewane

3

beginthreadexdaje ci wątek HANDLEdo wykorzystania w WaitForSingleObjecti znajomych. beginthreadnie. Nie zapomnij, CloseHandle()kiedy skończysz. Prawdziwą odpowiedzią byłoby użycie boost::threadlub wkrótce C ++ 09 klasy wątku.


Opis msdn mówi, że „Jeśli się powiedzie, każda z tych funkcji zwraca uchwyt do nowo utworzonego wątku;” odwołując się do _beginthread () i _beginthreadex () ...
Kiril,

@Kiril: ale dokumentacja mówi dalej, że _beginthread zamyka uchwyt za Ciebie, co oznacza, że ​​nie możesz go używać, jeśli wątek szybko się kończy ...
Roger Lipscombe

2

W porównaniu _beginthreadz _beginthreadex, możesz:

  1. Określ atrybuty zabezpieczeń.
  2. Rozpocznij wątek w stanie zawieszonym.
  3. Możesz uzyskać identyfikator wątku, którego można używać z OpenThread.
  4. Zwrócony uchwyt wątku jest gwarantowany, jeśli wywołanie zakończyło się pomyślnie. Tam musisz zamknąć uchwyt CloseHandle.
  5. Zwrócony uchwyt wątku może być używany z synchronizacyjnymi interfejsami API.

To _beginthreadexbardzo przypomina CreateThread, ale pierwsza to implementacja CRT, a druga wywołanie Windows API. Dokumentacja narzędzia CreateThread zawiera następujące zalecenia:

Wątek w pliku wykonywalnym, który wywołuje bibliotekę wykonawczą języka C (CRT), powinien używać funkcji _beginthreadexi _endthreadexdo zarządzania wątkami, a nie CreateThreadi ExitThread; wymaga to użycia wielowątkowej wersji CRT. Jeśli wątek utworzony przy użyciu CreateThreadwywołań CRT, CRT może zakończyć proces w warunkach małej ilości pamięci.


Zgodnie ze specyfikacją API, wypunktowanie 3–5 nie jest unikalne dla _beginthreadex. Możesz rzutować uintptr_tpowrót z obu funkcji na HANDLE.
Andon M. Coleman

Tak, w teorii masz rację. W praktyce różnica polega na tym, _beginthreadże przy wyjściu zamyka klamkę. Nie możesz więc niezawodnie używać uchwytu z synchronizacyjnymi interfejsami API lub uzyskać identyfikator wątku, dopóki nie użyjesz innego sposobu synchronizacji i zduplikowania uchwytu. Ale potem _beginthreadexrobi to za Ciebie.
Vishal

2

CreateThread()kiedyś było nie-nie, ponieważ CRT byłby nieprawidłowo inicjowany / czyszczony. Ale to już historia: można teraz (używając VS2010 i prawdopodobnie kilku wersji wstecz) dzwonić CreateThread()bez zrywania CRT.

Oto oficjalne potwierdzenie MS . Stanowi jeden wyjątek:

Właściwie jedyną funkcją, której nie należy używać w wątku utworzonym za pomocą, CreateThread()jest signal()funkcja.

Jednak z punktu widzenia spójności osobiście wolę nadal używać _beginthreadex().


Chociaż przypuszczam, że to prawda, czy możesz podać miarodajne dowody - łącząc się z dokumentacją MS lub analizując źródła CRT _beginthreadex / _endthreadex?
Suma

@Suma, myślę, że dodałem link do MS, kiedy
pisałeś

Dokument, do którego tworzysz łącze, nie wydaje się potwierdzać: „Jednak w zależności od wywoływanych funkcji CRT może wystąpić niewielki wyciek pamięci po zakończeniu wątków”. Oznacza to, że nie jest to już duże i ogólne nie-nie, ale nadal nie-nie, jeśli często tworzysz wątki i używasz w nich tych funkcji. Jednak dokument pochodzi z 2005 r. I dlatego nie może odnosić się do aktualnego stanu sprawy.
Suma,

1
Hmm ... chociaż może to zależeć od przypadku użycia, funkcji pozostawiającej wyciek pamięci, bez względu na rozmiar, rozważałbym odpowiedź nie-nie ... - w szczególności, jeśli istnieje nie przeciekająca alternatywa!
alk

„Można teraz wywołać CreateThread () bez przerywania CRT”. - Niestety, to nieprawda i nigdy nie była. From CreateThread : „Wątek w pliku wykonywalnym, który wywołuje bibliotekę wykonawczą języka C (CRT), powinien używać funkcji _beginthreadex i _endthreadex do zarządzania wątkami [...] Jeśli wątek utworzony za pomocą funkcji CreateThread wywołuje CRT, CRT może zakończyć działanie proces w warunkach małej ilości pamięci. "
Niespodziewane

2

CreateThread()to wywołanie interfejsu API systemu Windows, które jest neutralne językowo. Po prostu tworzy obiekt systemu operacyjnego - wątek i zwraca HANDLE do tego wątku. Wszystkie aplikacje systemu 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 interfejsu API musisz wykonać pewne czynności konserwacyjne: przekonwertować parametry i wyniki, przydzielić tymczasową pamięć itp.

_beginthreadex()jest opakowaniem C, CreateThread()które jest specyficzne dla języka C. Umożliwia pracę oryginalnych jednowątkowych 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 CreateThread(). Jeśli używasz CRT, musisz użyć _beginthreadex()lub niektóre f-ns z ciągiem CRT mogą nie działać poprawnie przed VC2005.


1

CreateThread()jest prostym wywołaniem systemowym. Jest zaimplementowany, na Kernel32.dllktórym najprawdopodobniej Twoja aplikacja będzie już połączona z innymi powodami. Jest zawsze dostępny w nowoczesnych systemach Windows.

_beginthread()i _beginthreadex()są funkcjami opakowania w Microsoft C Runtime ( msvcrt.dll). Różnice między tymi dwoma zaproszeniami są opisane w dokumentacji. Jest zatem dostępny, gdy środowisko wykonawcze języka Microsoft C jest dostępne lub jeśli aplikacja jest z nim połączona statycznie. Prawdopodobnie będziesz również łączyć się z tą biblioteką, chyba że kodujesz w czystym interfejsie API systemu Windows (co osobiście często robię).

Twoje pytanie jest spójne i faktycznie powracające. Podobnie jak wiele interfejsów API, mamy do czynienia z zduplikowanymi i niejednoznacznymi funkcjami w interfejsie Windows API. Co najgorsze, dokumentacja nie wyjaśnia sprawy. Przypuszczam, że _beginthread()rodzina funkcji została stworzona w celu lepszej integracji z innymi standardowymi funkcjami C, takimi jak manipulacja errno. _beginthread()dzięki temu lepiej integruje się ze środowiskiem wykonawczym C.

Mimo to, chyba że masz dobre powody, aby używać _beginthread()lub _beginthreadex(), powinieneś używać CreateThread(), głównie dlatego, że możesz uzyskać jedną zależność od biblioteki mniej w swoim ostatecznym pliku wykonywalnym (a dla MS CRT ma to trochę znaczenia). Nie masz również zawijającego się kodu wokół wywołania, chociaż ten efekt jest pomijalny. Innymi słowy, uważam, że głównym powodem, dla CreateThread()którego warto się trzymać, jest to, że nie ma dobrego powodu, _beginthreadex()aby zacząć od tego. Funkcjonalności są dokładnie lub prawie takie same.

Jednym z dobrych powodów użycia _beginthread() byłoby (jak się wydaje fałszywe), że obiekty C ++ byłyby prawidłowo rozwijane / niszczone, gdyby _endthread()zostały wywołane.


Brak niejednoznaczne wywołania funkcji w ogóle . CreateThreadto wywołanie interfejsu API systemu Windows w celu utworzenia wątku. Jeśli używasz CRT (ponieważ programujesz w C lub C ++), powinieneś utworzyć wątki przy użyciu _beginthread[ex]wywołań CRT (które CreateThreadoprócz wykonania niezbędnej inicjalizacji CRT). Najważniejsza różnica między _beginthreadi byłym wariantem: pierwszy zachowuje własność natywnego uchwytu wątku, podczas gdy drugi przekazuje własność wywołującemu.
Niespodziewane

Nitpick: niemsvcrt.dll jest biblioteką DLL środowiska wykonawczego C! Zobacz blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273
Govind Parmar

0

Inne odpowiedzi nie omawiają konsekwencji wywołania funkcji czasu wykonywania C, która otacza funkcję API Win32. Jest to ważne przy rozważaniu sposobu blokowania modułu ładującego DLL.

Niezależnie od tego, czy _beginthread{ex}jakikolwiek specjalny sposób zarządzania pamięcią wątków / światłowodów środowiska wykonawczego języka C, o którym mowa w innych odpowiedziach, jest zaimplementowany w (zakładając dynamiczne łączenie z programem C Runtime) w bibliotece DLL, której procesy mogły jeszcze nie zostać załadowane.

To nie jest bezpieczne, aby zadzwonić _beginthread*z DllMain. Przetestowałem to, pisząc bibliotekę DLL załadowaną przy użyciu funkcji „AppInit_DLLs” systemu Windows. Wywołanie _beginthreadex (...)zamiast CreateThread (...)powoduje, że WIELE ważnych części systemu Windows przestaje działać podczas rozruchu jako DllMainzakleszczenie punktu wejścia, czekające na zwolnienie blokady modułu ładującego w celu wykonania pewnych zadań inicjalizacyjnych.

Nawiasem mówiąc, jest to również powód, dla którego kernel32.dll ma wiele nakładających się funkcji tekstowych, które ma również środowisko wykonawcze C - użyj tych z, DllMainaby uniknąć tego samego rodzaju sytuacji.


0

Jeśli przeczytasz w niej książkę Debugowanie aplikacji systemu Windows od Jeffreya Richtera, wyjaśnia on, że prawie we wszystkich przypadkach musisz dzwonić _beginthreadexzamiast dzwonić CreateThread. _beginthreadto tylko uproszczone opakowanie _beginthreadex.

_beginthreadexinicjuje pewne wewnętrzne funkcje CRT (C RunTime), których CreateThreadinterfejs API nie zrobiłby.

Konsekwencją korzystania z CreateThreadinterfejsu API zamiast używania _begingthreadexwywołań funkcji CRT może być nieoczekiwana przyczyna problemów.

Sprawdź ten stary Microsoft Journal od Richtera.


0

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żyjesz _beginthread zamiast _beginthreadex, spowoduje to błąd zbyt wielu argumentów dla _beginthread, ponieważ _beginthread nie mógł utworzyć wątku z atrybutem bezpieczeństwa, a także myślę, że _beginthread jest niepotrzebny, możesz bezwzględnie użyć * (_ beginthreadex) i CreateThread


-2

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

Wszystkie komentarze dotyczące wycieków pamięci itp. Dotyczą bardzo starych wersji <VS2005. Wiele lat temu przeprowadziłem testy warunków skrajnych i mogę obalić ten mit. Nawet Microsoft miesza style w swoich przykładach, prawie nigdy nie używając _beginthread.


CreateThread : "Jeśli wątek utworzony za pomocą CreateThread wywołuje CRT, CRT może zakończyć proces w warunkach małej ilości pamięci."
Niespodziewane

Opierając się na stwierdzeniu, że „wymaga użycia wielowątkowej wersji CRT”, zakładam, że jest to śmieci dokumentacji, ponieważ nie ma już wielowątkowej wersji CRT i od wielu lat.
Lothar

„nie ma już wielowątkowej wersji crt” - MSDN twierdzi, że „jednowątkowy CRT nie jest już dostępny”. Nie możecie mieć racji oboje. Tutaj też pójdę z MSDN.
Niespodziewane

To była literówka, oczywiście miałem na myśli, że nie ma jednego wątku, a wielowątkowość stała się standardem, a to, co zniknęło, to różnica między używaniem lub nie używaniem wątków.
Lothar

To robi się naprawdę dziwne. Używasz teraz stwierdzenia, które jest niewątpliwie poprawne ( „wymaga użycia wielowątkowej wersji CRT” ), aby twierdzić, że zarówno to stwierdzenie, jak i pozostała część dokumentacji są najprawdopodobniej błędne? To z pewnością nie brzmi dobrze.
Niespodziewane
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.