Was ist ein besserer Weg, um einen Thread zu starten _beginthread
, _beginthreadx
oder CreateThread
?
Ich versuche , um zu bestimmen , was sind die Vorteile / Nachteile _beginthread
, _beginthreadex
und 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?
quelle
Antworten:
CreateThread()
ist ein roher Win32-API-Aufruf zum Erstellen eines weiteren Steuerungsthreads auf Kernelebene._beginthread()
&_beginthreadex()
sind C-Laufzeitbibliotheksaufrufe, dieCreateThread()
hinter den Kulissen aufrufen . NachCreateThread()
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).quelle
_begin
Routinen interne Aufrufe sind undCreateThread
die 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._begin
Funktionen 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.CreateThread
und 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 GleitkommaumgebungCreateThread
zum 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."Es gibt verschiedene Unterschiede zwischen
_beginthread()
und_beginthreadex()
._beginthreadex()
wurde gemacht, um mehr wie zu handelnCreateThread()
(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: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ückgegebenGetCurrentPackageId()
wird), initialisiert die Laufzeit den MTA auf dem neu erstellten Thread.quelle
CreateThread
, das Richtige zu nennen , sind immer geringer.Im Allgemeinen ist es richtig,
_beginthread()/_endthread()
(oder dieex()
Varianten) anzurufen . Wenn Sie jedoch die CRT als DLL verwenden, wird der CRT - Zustand richtig , wie die CRT initialisiert und zerstört werdenDllMain
wird aufgerufen werden ,DLL_THREAD_ATTACH
undDLL_THREAD_DETACH
wenn Sie anrufenCreateThread()
undExitThread()
oder die Rückkehr ist.Der
DllMain
Code für die CRT befindet sich im Installationsverzeichnis für VS unter VC \ crt \ src \ crtlib.c.quelle
Dies ist der Code im Kern von
_beginthreadex
(siehecrt\src\threadex.c
):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.quelle
Sie sollten
_beginthread
oder 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
_beginthread
Sie nicht anrufen,CloseHandle
wie 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 vonCloseHandle
. 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.CloseHandle
Muss aufgerufen werden.quelle
CreateThread()
Früher gab es Speicherlecks, wenn Sie CRT-Funktionen in Ihrem Code verwenden._beginthreadex()
hat die gleichen Parameter wieCreateThread()
und ist vielseitiger als_beginthread()
. Also empfehle ich Ihnen zu verwenden_beginthreadex()
.quelle
CreateThread
geht nie verloren . Es ist eher die CRT, die dies tut, wenn sie von einem Thread aufgerufen wird, der nicht ordnungsgemäß initialisiert wurde.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()
:quelle
Das Betrachten der Funktionssignaturen
CreateThread
ist fast identisch mit_beginthreadex
._beginthread
,_beginthreadx
vs.CreateThread
In den Anmerkungen hier heißt
_beginthread
es, dass entweder__cdecl
oder__clrcall
die Aufrufkonvention als Startpunkt verwendet werden kann und_beginthreadex
entweder__stdcall
oder als Startpunkt verwendet werden kann__clrcall
.Ich denke, alle Kommentare, die zu Speicherlecks abgegeben wurden,
CreateThread
sind über ein Jahrzehnt alt und sollten wahrscheinlich ignoriert werden.Interessanterweise
_beginthread*
rufen beide Funktionen tatsächlichCreateThread
unter der HaubeC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
auf meinem Computer auf.quelle
beginthreadex
gibt dir einen ThreadHANDLE
zur Verwendung inWaitForSingleObject
und Freunde.beginthread
nicht. Vergiss nicht,CloseHandle()
wenn du fertig bist. Die eigentliche Antwort wäre,boost::thread
die Thread-Klasse von C ++ 09 zu verwenden oder bald.quelle
Im Vergleich zu
_beginthread
können_beginthreadex
Sie mit:OpenThread
.CloseHandle
.Das
_beginthreadex
ähnelt starkCreateThread
, aber das erstere ist eine CRT-Implementierung und das letztere ein Windows-API-Aufruf. Die Dokumentation zu CreateThread enthält die folgende Empfehlung:quelle
_beginthreadex
. Sie können dieuintptr_t
Rückgabe von beiden Funktionen an umwandelnHANDLE
._beginthread
der 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_beginthreadex
macht es das für dich.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:
Aus konsistenter Sicht bevorzuge ich es jedoch persönlich, weiter zu verwenden
_beginthreadex()
.quelle
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-WrapperCreateThread()
, 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.quelle
CreateThread()
ist der direkte Systemaufruf. Es ist implementiert, aufKernel32.dll
dem 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 wurdeerrno
._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 verwendenCreateThread()
, 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 daranCreateThread()
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.quelle
CreateThread
ist 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 (dieCreateThread
zusätzlich zur Durchführung der erforderlichen CRT-Initialisierung aufrufen ). Der wichtigste Unterschied zwischen_beginthread
und der Ex-Variante: Ersteres behält das Eigentum am nativen Thread-Handle, während letzteres das Eigentum an den Aufrufer übergibt.msvcrt.dll
ist nicht die C-Laufzeit-DLL! Siehe blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273In 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*
ausDllMain
. 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 dieDllMain
Deadlocks 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.quelle
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,
_beginthreadex
anstatt anzurufenCreateThread
._beginthread
ist nur eine vereinfachte Hülle herum_beginthreadex
._beginthreadex
Initialisiert bestimmte CRT-Interna (C RunTime), die dieCreateThread
API nicht ausführen würde.Eine Konsequenz, wenn Sie die
CreateThread
API anstelle von_begingthreadex
Aufrufen von CRT-Funktionen verwenden, kann unerwartete Probleme verursachen.Schauen Sie sich dieses alte Microsoft Journal von Richter an.
quelle
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.
quelle