Windows-Threading: _beginthread vs _beginthreadex vs CreateThread C ++

133

Was ist ein besserer Weg, um einen Thread zu starten _beginthread, _beginthreadxoder CreateThread?

Ich versuche , um zu bestimmen , was sind die Vorteile / Nachteile _beginthread, _beginthreadexund CreateThread. All diese Funktionen einen Thread - Handle zu einem neu erstellten Thread zurückkehren, ich weiß schon , dass Createthread eine wenig zusätzliche Informationen bereitstellt , wenn ein Fehler auftritt (es durch den Aufruf überprüft werden kann GetLastError) ... aber was einige Dinge soll ich beachten , wenn ich‘ Verwenden Sie diese Funktionen?

Ich arbeite mit einer Windows-Anwendung, sodass eine plattformübergreifende Kompatibilität bereits nicht in Frage kommt.

Ich habe die msdn-Dokumentation durchgesehen und kann zum Beispiel einfach nicht verstehen, warum sich jemand dafür entscheiden würde, _beginthread anstelle von CreateThread zu verwenden oder umgekehrt.

Prost!

Update: OK, danke für all die Infos, ich habe auch an einigen Stellen gelesen, die ich nicht anrufen kann, WaitForSingleObject()wenn ich sie benutze _beginthread(), aber wenn ich _endthread()den Thread anrufe, sollte das nicht funktionieren? Was ist dort los?

Kiril
quelle
2
Hier ist eine Analyse dessen, was _beginthreadex () für C / C ++ - Programmierer tut , die ich über einen Link auf der Website von Eli Bendersky gefunden habe. Dies geht aus einer Frage und Antwort hervor, ob CreateThread () verwendet werden soll oder nicht. microsoft.com/msj/0799/win32/win320799.aspx
Richard Chambers

Antworten:

96

CreateThread() ist ein roher Win32-API-Aufruf zum Erstellen eines weiteren Steuerungsthreads auf Kernelebene.

_beginthread()& _beginthreadex()sind C-Laufzeitbibliotheksaufrufe, die CreateThread()hinter den Kulissen aufrufen . Nach CreateThread()der Rückkehr _beginthread/ex()kümmert sich zusätzliche Buchhaltung, um die C-Laufzeitbibliothek im neuen Thread nutzbar und konsistent zu machen.

In C ++ sollten Sie mit ziemlicher Sicherheit verwenden, es _beginthreadex()sei denn, Sie stellen überhaupt keine Verknüpfung zur C-Laufzeitbibliothek her (auch bekannt als MSVCRT * .dll / .lib).

Drew Hall
quelle
39
Dies ist nicht mehr ganz so wahr wie früher. Die CRT funktioniert in einem von CreateThread () erstellten Thread mit Ausnahme der Funktion signal () ordnungsgemäß. Für jeden mit CreateThread () erstellten Thread, der die CRT verwendet, tritt ein kleiner Speicherverlust (~ 80 Byte) auf, der jedoch ordnungsgemäß funktioniert. Weitere Informationen finden Sie unter: support.microsoft.com/default.aspx/kb/104641
John Dibling
1
@ John: Eigentlich gilt dieser Fehler nur bis zu MSVC ++ 6.0
Bobobobo
5
@ Bobobobo: Gute Frage. Ich kann nur spekulieren, dass MS ursprünglich beabsichtigte, dass die _beginRoutinen interne Aufrufe sind und CreateThreaddie API-Funktion sein sollte, die jeder aufrufen würde. Eine weitere mögliche Erklärung ist, dass MS eine lange und glorreiche Geschichte darin hat, den Standard zu ignorieren und sehr schlechte Entscheidungen über die Benennung von Dingen zu treffen.
John Dibling
15
Die _beginFunktionen beginnen mit einem Unterstrich, da Microsoft den Standard genauer befolgt. In der C-Laufzeit sind Namen mit Unterstrich für die Implementierung reserviert (und die Implementierung kann sie wie bei diesen für den Endbenutzer dokumentieren). beginthreadex()ist ein Name, den der Benutzer verwenden darf. Wenn die C-Laufzeit es verwendet, kann es zu Konflikten mit einem Endbenutzersymbol kommen, für das der Benutzer ein legitimes Recht hatte, mit der Verwendung rechnen zu können. Beachten Sie, dass Win32-APIs nicht Teil der C-Laufzeit sind und den Namespace des Benutzers verwenden.
Michael Burr
2
@Lothar: Es gibt Unterschiede zwischen dem Win32-API-Aufruf CreateThreadund den CRT-Aufrufen _beginthread/ex. Wenn Sie die CRT in einem Thread aufrufen, sollte sie immer mit erstellt werden _beginthread/ex. Andernfalls treten möglicherweise keine Speicherlecks mehr auf. Aber Sie werden Ihre Gleitkommaumgebung CreateThreadzum Beispiel beim Aufrufen sicherlich nicht richtig initialisieren . Es gibt noch mehr : "Wenn ein mit CreateThread erstellter Thread die CRT aufruft, kann die CRT den Prozess unter Bedingungen mit wenig Arbeitsspeicher beenden."
Unsichtbarer
37

Es gibt verschiedene Unterschiede zwischen _beginthread()und _beginthreadex(). _beginthreadex()wurde gemacht, um mehr wie zu handeln CreateThread()(in beiden Parametern und wie es sich verhält).

Wie Drew Hall erwähnt, müssen Sie bei Verwendung der C / C ++ - Laufzeit _beginthread()/ _beginthreadex()anstelle von / verwenden, CreateThread()damit die Laufzeit eine eigene Thread-Initialisierung durchführen kann (Einrichten des lokalen Thread-Speichers usw.).

In der Praxis bedeutet dies, dass CreateThread()Ihr Code so gut wie nie direkt von Ihrem Code verwendet werden sollte.

Die MSDN-Dokumente für _beginthread()/ enthalten _beginthreadex()einige Details zu den Unterschieden. Eine der wichtigeren ist, dass das Thread-Handle für einen von erstellten _beginthread()Thread beim Beenden des Threads automatisch von der CRT geschlossen wird, "wenn der von _beginthread generierte Thread beendet wird schnell ist das an den Aufrufer von _beginthread zurückgegebene Handle möglicherweise ungültig oder zeigt, schlimmer noch, auf einen anderen Thread. "

Hier ist, was die Kommentare _beginthreadex()in der CRT-Quelle zu sagen haben:

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

Update Jan 2013:

In der CRT für VS 2012 wird ein zusätzliches Initialisierungsbit ausgeführt _beginthreadex(): Wenn es sich bei dem Prozess um eine "gepackte App" handelt (wenn etwas Nützliches zurückgegeben GetCurrentPackageId()wird), initialisiert die Laufzeit den MTA auf dem neu erstellten Thread.

Michael Burr
quelle
3
Es gibt angemessene Zeiten, in denen CreateThread () gerechtfertigt ist, aber ehrlich gesagt müssen Sie wirklich alles tun, um dies zu tun. Wir sprechen von einem völligen Mangel an tragbaren Geräten und schreiben eine exklusive WIN32-API-DLL oder -App. Einschließlich keiner C-Laufzeitaufrufe. Selbst die STL-Nutzung ist insofern eingeschränkt, als Sie benutzerdefinierte Allokatoren bereitstellen müssen, um die WIN32-Speicherverwaltungsfunktionen verwenden zu können. Das Setup, um dies mit Developer Studio zu tun, ist eine Aufgabe für sich, aber für eine einzige WIN32-Bibliothek mit möglichst geringem Platzbedarf ist dies möglich. Aber ja, es ist nicht verdammt wahrscheinlich für fast alle, außer für einige sehr wenige.
WhozCraig
1
@ WhozCraig: Es gibt strengere Einschränkungen beim Weglassen der CRT. Die bekanntesten sind: Keine 64-Bit-Ganzzahlunterstützung, keine Gleitkommaunterstützung und - am drastischsten - keine Ausnahmebehandlung. Das bedeutet wirklich keine Ausnahmebehandlung - überhaupt nicht . Nicht einmal SEH-Ausnahmen. Dies ist besonders schwer auszugleichen, und die Chancen CreateThread, das Richtige zu nennen , sind immer geringer.
Unsichtbarer
@MichaelBurr: Möglicherweise möchten Sie Ihre Antwort für VC ++ 2015 aktualisieren .
user541686
@Mehrdad: Welche Änderungen sind für Sie besonders erwähnenswert?
Unsichtbar
Ich habe festgestellt, dass DisableThreadLibraryCalls keine Auswirkungen auf mit CreateThread erstellte Threads hat, jedoch mit _beginthread oder _beginthreadex erstellte Threads deaktiviert.
SPlatten
23

Im Allgemeinen ist es richtig, _beginthread()/_endthread()(oder die ex()Varianten) anzurufen . Wenn Sie jedoch die CRT als DLL verwenden, wird der CRT - Zustand richtig , wie die CRT initialisiert und zerstört werden DllMainwird aufgerufen werden , DLL_THREAD_ATTACHund DLL_THREAD_DETACHwenn Sie anrufen CreateThread()und ExitThread()oder die Rückkehr ist.

Der DllMainCode für die CRT befindet sich im Installationsverzeichnis für VS unter VC \ crt \ src \ crtlib.c.

MSN
quelle
Toller Ausgangspunkt. Mit ein wenig Debugging kann man zeigen, dass __CRTDLL_INIT auch für eine statisch verknüpfte CRT aufgerufen wird. Callstack Der Init wird von _LdrpCallInitRoutine @ 16 () aufgerufen. Ich bin mir nicht sicher, durch welchen Mechanismus. Dies bedeutet, dass bei der letzten CRT die gesamte Initialisierung / Deinitialisierung korrekt durchgeführt wird, mit Ausnahme der Signalbehandlung, die weiterhin in der Hilfsfunktion _threadstartex ausgeführt wird, die von Beginthread aufgerufen wird, jedoch nicht von CreateThread. Vielleicht könnten Sie dies in die Antwort aufnehmen und ich werde das Kopfgeld vergeben?
Suma
Kopfgeld vergeben, da dies am hilfreichsten erscheint. Dennoch könnte die Antwort möglicherweise eine Aktualisierung wert sein. Wenn Sie es nicht tun können, werde ich es möglicherweise innerhalb weniger Tage erneut besuchen.
Suma
1
@MSN: Bitte beachten Sie, dass CreateThread in einer DLL immer noch fehlerhaft ist, wenn Sie eine Verknüpfung mit der statischen CRT herstellen und DisableThreadLibraryCalls aufgerufen haben, wodurch die Aufrufe für DLL_THREAD_DETACH deaktiviert werden. Dann werden Sie Speicherlecks bekommen. Dies ist hier in meinem KB-Artikel dokumentiert: support.microsoft.com/kb/555563/en-us
Jochen Kalmbach
17

Dies ist der Code im Kern von _beginthreadex(siehe 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;
    }

Der Rest von _beginthreadex initialisiert die Datenstruktur pro Thread für CRT.

Der Vorteil der Verwendung _beginthread*ist, dass Ihre CRT-Aufrufe vom Thread korrekt funktionieren.

Constantin
quelle
12

Sie sollten _beginthreadoder verwenden_beginthreadex zulassen, dass die C-Laufzeitbibliothek den Thread selbst initialisiert. Nur C / C ++ - Programmierer müssen dies wissen, da sie nun die Regeln für die Verwendung ihrer eigenen Entwicklungsumgebung einhalten sollten.

Wenn Sie verwenden, müssen _beginthreadSie nicht anrufen, CloseHandlewie es die RTL für Sie erledigt. Aus diesem Grund können Sie nicht auf den Griff warten, wenn Sie ihn verwendet haben _beginthread. Ebenfalls_beginthread führt auch zu Verwirrung, wenn die Thread-Funktion sofort (schnell) beendet wird, da der Start-Thread ein ungültiges Thread-Handle für den gerade gestarteten Thread enthält.

_beginthreadex Handles können zum Warten verwendet werden, erfordern jedoch auch einen expliziten Aufruf von CloseHandle . Dies ist Teil dessen, was sie für die Verwendung mit Wartezeit sicher macht. Ein weiteres Problem, um es absolut kinderleicht zu machen, besteht darin, den Thread immer angehalten zu starten. Überprüfen Sie den Erfolg, das Aufzeichnungshandle usw. Der Wiederaufnahme-Thread. Dies ist erforderlich, um zu verhindern, dass ein Thread beendet wird, bevor der startende Thread sein Handle aufzeichnen kann.

Die beste Vorgehensweise ist, zu verwenden _beginthreadex, den Start zu starten und nach dem Aufzeichnen des Handles fortzufahren. Das Warten auf das Handle ist in Ordnung. CloseHandleMuss aufgerufen werden.

jarcher7
quelle
8

CreateThread()Früher gab es Speicherlecks, wenn Sie CRT-Funktionen in Ihrem Code verwenden. _beginthreadex()hat die gleichen Parameter wie CreateThread()und ist vielseitiger als _beginthread(). Also empfehle ich Ihnen zu verwenden _beginthreadex().

Jaywalker
quelle
2
1999 Artikel, kann seitdem behoben worden sein
Bobobobo
1
Dieser Artikel aus dem Jahr 2005 bestätigt immer noch, dass ein Problem vorliegt.
Jaywalker
2
Ja, dies gilt nur für MSVC ++ 6.0 Service Pack 5 und früher. (siehe erweiterbare Dropdown-Liste "Gilt für"). Dies ist heute kein Problem, wenn Sie VC7 oder höher verwenden.
Bobobobo
1
Dies ist immer noch ein Problem, wenn Sie gegen die statische CRT verlinken! Es ist auch immer noch ein Problem, wenn Sie DisableThreadLibraryCalls in einer DLL aufrufen, die statisch verknüpft ist. Siehe meinen KB-Artikel: support.microsoft.com/kb/555563/en-us
Jochen Kalmbach
2
Sie haben die Informationen falsch dargestellt: Der Speicher CreateThreadgeht nie verloren . Es ist eher die CRT, die dies tut, wenn sie von einem Thread aufgerufen wird, der nicht ordnungsgemäß initialisiert wurde.
Unsichtbarer
6

Zu Ihrer aktualisierten Frage: "Ich habe auch an einigen Stellen gelesen, dass ich nicht anrufen kann, WaitForSingleObject()wenn ich sie verwendet habe _beginthread(), aber wenn ich _endthread()den Thread anrufe, sollte das nicht funktionieren?"

Im Allgemeinen können Sie ein Thread-Handle an WaitForSingleObject()(oder andere APIs, die auf Objekthandles warten) übergeben, um es zu blockieren, bis der Thread abgeschlossen ist. Das von erstellte Thread-Handle wird _beginthread()jedoch geschlossen, wenn _endthread()es aufgerufen wird (dies kann explizit oder implizit bis zur Laufzeit erfolgen, wenn die Thread-Prozedur zurückkehrt).

Das Problem wird in der Dokumentation genannt für WaitForSingleObject():

Wenn dieses Handle geschlossen ist, während die Wartezeit noch aussteht, ist das Verhalten der Funktion undefiniert.

Michael Burr
quelle
5

Das Betrachten der Funktionssignaturen CreateThreadist fast identisch mit _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 
);

In den Anmerkungen hier heißt _beginthreades, dass entweder __cdecloder __clrcalldie Aufrufkonvention als Startpunkt verwendet werden kann und _beginthreadexentweder __stdcalloder als Startpunkt verwendet werden kann __clrcall.

Ich denke, alle Kommentare, die zu Speicherlecks abgegeben wurden, CreateThreadsind über ein Jahrzehnt alt und sollten wahrscheinlich ignoriert werden.

Interessanterweise _beginthread*rufen beide Funktionen tatsächlich CreateThreadunter der Haube C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\srcauf meinem Computer auf.

// 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;
}
Bobobobo
quelle
2
Kommentar, warum Sie CreateThread nicht aufrufen und CRT-Aufrufe für diesen Thread einmischen sollten (definitiv kein Jahrzehnt alt und definitiv nicht ignoriert) : "Wenn ein mit CreateThread erstellter Thread die CRT aufruft, kann die CRT den Prozess in beenden Bedingungen mit wenig Speicher. "
Unsichtbarer
3

beginthreadexgibt dir einen Thread HANDLEzur Verwendung in WaitForSingleObjectund Freunde. beginthreadnicht. Vergiss nicht, CloseHandle()wenn du fertig bist. Die eigentliche Antwort wäre, boost::threaddie Thread-Klasse von C ++ 09 zu verwenden oder bald.

MD XF
quelle
In der msdn-Beschreibung heißt es: "Bei Erfolg gibt jede dieser Funktionen ein Handle an den neu erstellten Thread zurück." unter Bezugnahme auf _beginthread () und _beginthreadex () ...
Kiril
@Kiril: aber dann heißt es in der Dokumentation weiter, dass _beginthread das Handle für Sie schließt, was bedeutet, dass Sie es nicht verwenden können, wenn der Thread schnell beendet wird ...
Roger Lipscombe
2

Im Vergleich zu _beginthreadkönnen _beginthreadexSie mit:

  1. Geben Sie Sicherheitsattribute an.
  2. Starten Sie einen Thread im angehaltenen Zustand.
  3. Sie können die Thread-ID erhalten, mit der verwendet werden kann OpenThread.
  4. Das zurückgegebene Thread-Handle ist garantiert gültig, wenn der Aufruf erfolgreich war. Dort müssen Sie den Griff mit schließen CloseHandle.
  5. Das zurückgegebene Thread-Handle kann mit Synchronisations-APIs verwendet werden.

Das _beginthreadexähnelt stark CreateThread, aber das erstere ist eine CRT-Implementierung und das letztere ein Windows-API-Aufruf. Die Dokumentation zu CreateThread enthält die folgende Empfehlung:

Ein Thread in einer ausführbaren Datei, der die C-Laufzeitbibliothek (CRT) aufruft, sollte die Funktionen _beginthreadexund _endthreadexanstelle von CreateThreadund für die Thread-Verwaltung verwenden ExitThread. Dies erfordert die Verwendung der Multithread-Version der CRT. Wenn ein mit CreateThreadCRT erstellter Thread die CRT aufruft, kann die CRT den Prozess unter Bedingungen mit wenig Speicher beenden.

Vishal
quelle
Gemäß der API-Spezifikation sind die Aufzählungspunkte 3 bis 5 nicht eindeutig für _beginthreadex. Sie können die uintptr_tRückgabe von beiden Funktionen an umwandeln HANDLE.
Andon M. Coleman
Ja, Sie haben theoretisch Recht. In der Praxis besteht der Unterschied darin, dass _beginthreadder Griff beim Verlassen geschlossen wird. Daher können Sie das Handle nicht zuverlässig mit Synchronisations-APIs verwenden oder die Thread-ID abrufen, bis Sie das Handle auf andere Weise synchronisieren und duplizieren. Aber dann _beginthreadexmacht es das für dich.
Vishal
2

CreateThread()Einmal war ein Nein-Nein, weil die CRT falsch initialisiert / bereinigt wurde. Aber das ist jetzt Geschichte: Man kann jetzt (mit VS2010 und wahrscheinlich ein paar Versionen zurück) anrufen, CreateThread()ohne die CRT zu beschädigen .

Hier ist die offizielle MS-Bestätigung . Es gibt eine Ausnahme:

Die einzige Funktion, die in einem mit erstellten Thread nicht verwendet werden sollte, CreateThread()ist die signal()Funktion.

Aus konsistenter Sicht bevorzuge ich es jedoch persönlich, weiter zu verwenden _beginthreadex().

Serge Wautier
quelle
Ich nehme an, dass dies zutrifft, können Sie jedoch einige maßgebliche Beweise liefern - entweder durch Verknüpfung mit der MS-Dokumentation oder durch Analyse der CRT-Quellen _beginthreadex / _endthreadex?
Suma
@ Suma, ich denke, ich habe den MS-Link hinzugefügt, während Sie Ihren Kommentar eingegeben haben ;-)
Serge Wautier
Das Dokument, auf das Sie verlinken, scheint nicht zu bestätigen: "Je nachdem, welche CRT-Funktionen aufgerufen werden, kann es jedoch zu einem kleinen Speicherverlust kommen, wenn Threads beendet werden." Dies bedeutet, dass es kein großes und allgemeines Nein-Nein mehr ist, sondern immer noch ein Nein-Nein, wenn Sie häufig Threads erstellen und diese Funktionen in ihnen verwenden. Das Dokument stammt jedoch aus dem Jahr 2005 und kann daher nicht auf den aktuellen Stand der Angelegenheit eingehen.
Suma
1
Hmm ... obwohl es vom Anwendungsfall abhängen könnte, eine Funktion, die einen Speicherverlust hinterlässt, egal welcher Größe, würde ich ein Nein-Nein in Betracht ziehen ... - insbesondere, wenn es eine nicht leckende Alternative gibt!
Alk
"Man kann jetzt CreateThread () aufrufen, ohne die CRT zu beschädigen." - Leider ist dies nicht wahr und war es nie. Von CreateThread : "Ein Thread in einer ausführbaren Datei, die die C-Laufzeitbibliothek (CRT) aufruft, sollte die Funktionen _beginthreadex und _endthreadex für die Thread-Verwaltung verwenden. [...] Wenn ein mit CreateThread erstellter Thread die CRT aufruft, kann die CRT die beenden Prozess unter Bedingungen mit wenig Speicher. "
Unsichtbar
2

CreateThread()ist ein sprachneutraler Windows-API-Aufruf. Es wird nur ein OS-Objekt-Thread erstellt und HANDLE an diesen Thread zurückgegeben. Alle Windows-Anwendungen verwenden diesen Aufruf, um Threads zu erstellen. Alle Sprachen vermeiden direkte API-Aufrufe aus offensichtlichen Gründen: 1. Sie möchten nicht, dass Ihr Code betriebssystemspezifisch ist. 2. Sie müssen einige Hausarbeiten durchführen, bevor Sie API-like aufrufen: Parameter und Ergebnisse konvertieren, temporären Speicher zuweisen usw.

_beginthreadex()ist ein C-Wrapper CreateThread(), der C-spezifisch berücksichtigt. Es ermöglicht die Arbeit von Original-C f-ns mit einem Thread in einer Multithread-Umgebung, indem threadspezifischer Speicher zugewiesen wird.

Wenn Sie CRT nicht verwenden, können Sie einen direkten Anruf bei nicht vermeiden CreateThread(). Wenn Sie CRT verwenden, müssen Sie _beginthreadex()CRT-Zeichenfolgen verwenden, da f-ns vor VC2005 möglicherweise nicht ordnungsgemäß funktionieren.

SKV
quelle
1

CreateThread()ist der direkte Systemaufruf. Es ist implementiert, auf Kernel32.dlldem Ihre Anwendung höchstwahrscheinlich bereits aus anderen Gründen verlinkt ist. Es ist in modernen Windows-Systemen immer verfügbar.

_beginthread()und _beginthreadex()sind Wrapper-Funktionen in Microsoft C Runtime ( msvcrt.dll). Die Unterschiede zwischen den beiden Aufrufen sind in der Dokumentation angegeben. Es ist daher verfügbar, wenn die Microsoft C-Laufzeit verfügbar ist oder wenn Ihre Anwendung statisch damit verknüpft ist. Sie werden wahrscheinlich auch eine Verknüpfung zu dieser Bibliothek herstellen, es sei denn, Sie codieren in einer reinen Windows-API (wie ich es persönlich oft tue).

Ihre Frage ist kohärent und tatsächlich eine wiederkehrende. Wie bei vielen APIs gibt es in der Windows-API doppelte und mehrdeutige Funktionen, mit denen wir uns befassen müssen. Am schlimmsten ist, dass die Dokumentation das Problem nicht klärt. Ich nehme an, dass die _beginthread()Funktionsfamilie für eine bessere Integration mit anderen Standard-C-Funktionen wie der Manipulation von erstellt wurde errno._beginthread()Somit lässt sich die C-Laufzeit besser integrieren.

Trotzdem sollten Sie verwenden, es sei denn, Sie haben gute Gründe für die Verwendung von _beginthread()oder _beginthreadex(), sollten Sie diese verwenden CreateThread(), hauptsächlich, weil Ihre endgültige ausführbare Datei möglicherweise eine Bibliotheksabhängigkeit weniger aufweist (und für MS CRT ist dies ein wenig wichtig). Sie haben auch keinen Umbruchcode um den Anruf, obwohl dieser Effekt vernachlässigbar ist. Mit anderen Worten, ich glaube, dass der Hauptgrund für das Festhalten daran CreateThread()ist, dass es keinen guten Grund gibt, dies zu verwenden _beginthreadex(). Die Funktionen sind genau oder fast gleich.

Ein guter Grund für die Verwendung _beginthread() wäre (da es falsch zu sein scheint), dass C ++ - Objekte ordnungsgemäß abgewickelt / zerstört würden, wenn sie _endthread()aufgerufen würden.

Alecov
quelle
Es gibt keine mehrdeutige Funktion Anrufe überhaupt . CreateThreadist der Windows-API-Aufruf zum Erstellen eines Threads. Wenn Sie die CRT verwenden (weil Sie in C oder C ++ programmieren), sollten Sie Threads mithilfe der _beginthread[ex]Aufrufe der CRT erstellen (die CreateThreadzusätzlich zur Durchführung der erforderlichen CRT-Initialisierung aufrufen ). Der wichtigste Unterschied zwischen _beginthreadund der Ex-Variante: Ersteres behält das Eigentum am nativen Thread-Handle, während letzteres das Eigentum an den Aufrufer übergibt.
Unsichtbarer
Nitpick: msvcrt.dllist nicht die C-Laufzeit-DLL! Siehe blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273
Govind Parmar
0

In den anderen Antworten werden die Auswirkungen des Aufrufs einer C-Laufzeitfunktion, die eine Win32-API-Funktion umschließt, nicht erörtert. Dies ist wichtig, wenn Sie das Sperrverhalten des DLL-Loaders berücksichtigen.

Unabhängig davon, ob _beginthread{ex}eine spezielle C-Laufzeit-Thread- / Faserspeicherverwaltung durchgeführt wird oder nicht, wie in den anderen Antworten erläutert, wird sie in einer DLL implementiert (unter der Annahme einer dynamischen Verknüpfung mit der C-Laufzeit), die möglicherweise noch nicht geladen wurde.

Es ist nicht sicher anrufen _beginthread*aus DllMain. Ich habe dies getestet, indem ich eine DLL geschrieben habe, die mit der Windows-Funktion "AppInit_DLLs" geladen wurde. Das Aufrufen _beginthreadex (...)statt führt dazu, CreateThread (...)dass viele wichtige Teile von Windows während des Startvorgangs nicht mehr funktionieren, da die DllMainDeadlocks am Einstiegspunkt darauf warten, dass die Loader-Sperre freigegeben wird, um bestimmte Initialisierungsaufgaben auszuführen.

Dies ist übrigens auch der Grund, warum kernel32.dll viele überlappende Zeichenfolgenfunktionen hat, die auch die C-Laufzeit ausführt - verwenden Sie diese von DllMain, um die gleiche Situation zu vermeiden.

Andon M. Coleman
quelle
0

Wenn Sie das Buch Debuggen der Windows-Anwendung von Jeffrey Richter darin lesen, erklärt er, dass Sie fast in allen Fällen anrufen müssen, _beginthreadexanstatt anzurufen CreateThread. _beginthreadist nur eine vereinfachte Hülle herum _beginthreadex.

_beginthreadexInitialisiert bestimmte CRT-Interna (C RunTime), die die CreateThreadAPI nicht ausführen würde.

Eine Konsequenz, wenn Sie die CreateThreadAPI anstelle von _begingthreadexAufrufen von CRT-Funktionen verwenden, kann unerwartete Probleme verursachen.

Schauen Sie sich dieses alte Microsoft Journal von Richter an.

Ehsan Samani
quelle
-2

Es gibt keinen Unterschied mehr zwischen den beiden.

Alle Kommentare zu Speicherlecks usw. basieren auf sehr alten <VS2005-Versionen. Ich habe vor Jahren einige Stresstests durchgeführt und könnte diesen Mythos entlarven. Sogar Microsoft mischt die Stile in ihren Beispielen und verwendet fast nie _beginthread.

Lothar
quelle
CreateThread : "Wenn ein mit CreateThread erstellter Thread die CRT aufruft, kann die CRT den Prozess unter Bedingungen mit wenig Arbeitsspeicher beenden."
Unsichtbarer
Basierend auf der Subsentenz "erfordert die Verwendung der Multithread-Version der CRT" gehe ich davon aus, dass dies Dokumentationsmüll ist, da es seit vielen Jahren keine Multithread-CRT-Version mehr gibt.
Lothar
"Es gibt keine Multithread-CRT-Version mehr" - Der MSDN behauptet, dass "die Single-Threaded-CRT nicht mehr verfügbar ist". Sie können nicht beide Recht haben. Ich werde auch hier mit dem MSDN gehen.
Unsichtbarer
Es war ein Tippfehler, natürlich meinte ich, dass der einzelne Thread weg ist und der Multithread zum Standard geworden ist und was weg ist, ist die Unterscheidung zwischen der Verwendung oder Nichtverwendung von Threads.
Lothar
Das wird wirklich seltsam. Sie verwenden jetzt eine Aussage, die zweifellos richtig ist ( "erfordert die Verwendung der Multithread-Version der CRT" ), um zu behaupten, dass sowohl diese Aussage als auch der Rest der Dokumentation sehr wahrscheinlich falsch sind? Das klingt sicher nicht richtig.
Unsichtbarer