Quelle est la meilleure façon de démarrer un fil _beginthread
, _beginthreadx
ou CreateThread
?
J'essaie de déterminer quels sont les avantages / inconvénients de _beginthread
, _beginthreadex
et CreateThread
. Toutes ces fonctions renvoient un descripteur de thread à un thread nouvellement créé, je sais déjà que CreateThread fournit un peu d'informations supplémentaires lorsqu'une erreur se produit (cela peut être vérifié en appelant GetLastError
) ... mais que dois-je prendre en compte lorsque je ' m en utilisant ces fonctions?
Je travaille avec une application Windows, donc la compatibilité multiplateforme est déjà hors de question.
J'ai parcouru la documentation msdn et je ne peux tout simplement pas comprendre, par exemple, pourquoi quelqu'un déciderait d'utiliser _beginthread au lieu de CreateThread ou vice versa.
À votre santé!
Mise à jour: OK, merci pour toutes les informations, j'ai également lu dans quelques endroits que je ne peux pas appeler WaitForSingleObject()
si j'ai utilisé _beginthread()
, mais si j'appelle _endthread()
dans le fil de discussion, cela ne devrait-il pas fonctionner? Quel est le problème là-bas?
la source
Réponses:
CreateThread()
est un appel API Win32 brut pour créer un autre thread de contrôle au niveau du noyau._beginthread()
&_beginthreadex()
sont des appels de bibliothèque d'exécution C qui appellentCreateThread()
dans les coulisses. Une foisCreateThread()
retourné,_beginthread/ex()
s'occupe de la comptabilité supplémentaire pour rendre la bibliothèque d'exécution C utilisable et cohérente dans le nouveau thread.En C ++, vous devriez certainement utiliser à
_beginthreadex()
moins que vous ne fassiez pas du tout de lien vers la bibliothèque d'exécution C (aka MSVCRT * .dll / .lib).la source
_begin
routines soient des appels internes et qu'elleCreateThread
était censée être la fonction API que tout le monde appellerait. Une autre explication potentielle est que MS a une longue et glorieuse histoire d'ignorance de la norme et de très mauvaises décisions concernant la dénomination des choses._begin
fonctions commencent par un trait de soulignement car Microsoft a commencé à suivre de plus près la norme. Dans l'environnement d'exécution C, les noms comportant un trait de soulignement sont réservés pour l'implémentation (et l'implémentation peut les documenter pour une utilisation par l'utilisateur final, comme avec ceux-ci).beginthreadex()
est un nom que l'utilisateur peut utiliser. Si le moteur d'exécution C l'utilisait, il pourrait entrer en conflit avec un symbole d'utilisateur final que l'utilisateur avait le droit légitime de s'attendre à utiliser. Notez que les API Win32 ne font pas partie du runtime C et qu'elles utilisent l'espace de noms de l'utilisateur.CreateThread
et les appels CRT_beginthread/ex
, et lors de l'appel du CRT sur un thread, il doit toujours être créé avec_beginthread/ex
. Il se peut qu'il n'y ait plus de fuites de mémoire, sinon. Mais vous n'obtiendrez sûrement pas votre environnement en virgule flottante correctement initialisé lors d'un appelCreateThread
, par exemple. Il y a plus : "Si un thread créé à l'aide de CreateThread appelle le CRT, le CRT peut terminer le processus dans des conditions de mémoire insuffisante."Il existe plusieurs différences entre
_beginthread()
et_beginthreadex()
._beginthreadex()
a été fait pour agir plus commeCreateThread()
(dans les deux paramètres et comment il se comporte).Comme Drew Hall le mentionne, si vous utilisez le runtime C / C ++, vous devez utiliser
_beginthread()
/_beginthreadex()
au lieu deCreateThread()
pour que le runtime ait une chance d'effectuer sa propre initialisation de thread (configuration du stockage local des threads, etc.).En pratique, cela signifie que cela
CreateThread()
ne devrait pratiquement jamais être utilisé directement par votre code.Les documents MSDN pour
_beginthread()
/_beginthreadex()
ont pas mal de détails sur les différences - l'une des plus importantes est que puisque le handle de thread pour un thread créé par_beginthread()
se ferme automatiquement par le CRT lorsque le thread se termine, "si le thread généré par _beginthread se termine rapidement, le handle retourné à l'appelant de _beginthread peut être invalide ou, pire, pointer vers un autre thread ".Voici ce que
_beginthreadex()
disent les commentaires dans la source CRT:Mise à jour de janvier 2013:
Le CRT pour VS 2012 a un bit supplémentaire d'initialisation effectué dans
_beginthreadex()
: si le processus est une "application packagée" (si quelque chose d'utile est renvoyéGetCurrentPackageId()
), le runtime initialisera le MTA sur le thread nouvellement créé.la source
CreateThread
la bonne chose sont de moins en moins minces.En général, la bonne chose à faire est d'appeler
_beginthread()/_endthread()
(ou lesex()
variantes). Toutefois, si vous utilisez le CRT en tant que .dll, l'état CRT sera correctement initialisé et détruit comme le CRTDllMain
sera appelé avecDLL_THREAD_ATTACH
etDLL_THREAD_DETACH
lors de l'appelCreateThread()
etExitThread()
ou du retour, respectivement.Le
DllMain
code du CRT se trouve dans le répertoire d'installation de VS sous VC \ crt \ src \ crtlib.c.la source
C'est le code au cœur de
_beginthreadex
(voircrt\src\threadex.c
):Le reste
_beginthreadex
initialise la structure de données par thread pour CRT.L'avantage de l'utilisation
_beginthread*
est que vos appels CRT à partir de thread fonctionneront correctement.la source
Vous devez utiliser
_beginthread
ou_beginthreadex
pour permettre à la bibliothèque d'exécution C de faire sa propre initialisation du thread. Seuls les programmeurs C / C ++ ont besoin de le savoir car ils devraient désormais connaître les règles d'utilisation de leur propre environnement de développement.Si vous utilisez,
_beginthread
vous n'avez pas besoin d'appelerCloseHandle
comme le RTL le fera pour vous. C'est pourquoi vous ne pouvez pas attendre sur la poignée si vous avez utilisé_beginthread
. Conduit également_beginthread
à la confusion si la fonction de thread se termine immédiatement (rapidement) car le thread de lancement peut être laissé avec une poignée de thread non valide pour le thread qu'il vient de lancer._beginthreadex
les handles peuvent être utilisés pour wait mais nécessitent également un appel explicite àCloseHandle
. Cela fait partie de ce qui les rend sûrs pour une utilisation avec attente. Il y a un autre problème pour le rendre complètement infaillible est de toujours démarrer le thread suspendu. Vérifiez le succès, enregistrez la poignée, etc. Cela est nécessaire pour empêcher un thread de se terminer avant que le thread de lancement puisse enregistrer son handle.La meilleure pratique consiste à utiliser
_beginthreadex
, démarrer suspendu puis reprendre après l'enregistrement de la poignée, attendre que la poignée est OK,CloseHandle
doit être appelée.la source
CreateThread()
utilisé pour avoir des fuites de mémoire lorsque vous utilisez des fonctions CRT dans votre code._beginthreadex()
a les mêmes paramètres queCreateThread()
et il est plus polyvalent que_beginthread()
. Je vous recommande donc d'utiliser_beginthreadex()
.la source
CreateThread
ne perd jamais de mémoire. C'est plutôt le CRT qui le fait, lorsqu'il est appelé à partir d'un thread qui n'a pas été correctement initialisé.En ce qui concerne votre question mise à jour: "J'ai également lu dans quelques endroits que je ne peux pas appeler
WaitForSingleObject()
si j'ai utilisé_beginthread()
, mais si j'appelle_endthread()
dans le fil de discussion, cela ne devrait-il pas fonctionner?"En général, vous pouvez passer un handle de thread à
WaitForSingleObject()
(ou à d'autres API qui attendent sur les handles d'objet) pour bloquer jusqu'à ce que le thread soit terminé. Mais le handle de thread créé par_beginthread()
est fermé lorsqu'il_endthread()
est appelé (ce qui peut être fait explicitement ou est fait implicitement par le moment de l'exécution lorsque la procédure de thread retourne).Le problème est signalé dans la documentation pour
WaitForSingleObject()
:la source
En regardant les signatures de fonction,
CreateThread
est presque identique à_beginthreadex
._beginthread
,_beginthreadx
vsCreateThread
Les remarques ici indiquent que vous
_beginthread
pouvez utiliser la convention soit__cdecl
ou__clrcall
appeler comme point de départ, et_beginthreadex
peut utiliser soit__stdcall
ou__clrcall
comme point de départ.Je pense que tous les commentaires des gens sur les fuites de mémoire datent de
CreateThread
plus d'une décennie et devraient probablement être ignorés.Fait intéressant, les deux
_beginthread*
fonctions appellentCreateThread
sous le capot,C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
sur ma machine.la source
beginthreadex
vous donne un filHANDLE
à utiliser dansWaitForSingleObject
et vos amis.beginthread
pas. N'oubliez pas deCloseHandle()
quand vous avez terminé. La vraie réponse serait d'utiliserboost::thread
ou bientôt la classe de thread de C ++ 09.la source
Par rapport à
_beginthread
, avec_beginthreadex
vous pouvez:OpenThread
.CloseHandle
.Le
_beginthreadex
ressemble beaucoupCreateThread
, mais le premier est une implémentation CRT et le second un appel d'API Windows. La documentation de CreateThread contient la recommandation suivante:la source
_beginthreadex
. Vous pouvez convertir leuintptr_t
retour des deux fonctions enHANDLE
._beginthread
ferme la poignée en sortie. Vous ne pouvez donc pas utiliser de manière fiable le handle avec les API de synchronisation ou pour obtenir l'ID de thread jusqu'à ce que et à moins que vous n'utilisiez un autre moyen de synchroniser et de dupliquer le handle. Mais alors il y a le_beginthreadex
faire pour vous.CreateThread()
une fois était un non-non parce que le CRT serait incorrectement initialisé / nettoyé. Mais c'est maintenant de l'histoire: on peut maintenant (en utilisant VS2010 et probablement quelques versions en arrière) appelerCreateThread()
sans casser le CRT.Voici la confirmation officielle de MS . Il énonce une exception:
Cependant, du point de vue de la cohérence, je préfère personnellement continuer à utiliser
_beginthreadex()
.la source
CreateThread()
est un appel d'API Windows indépendant du langage. Il crée simplement un objet OS - thread et renvoie HANDLE à ce thread. Toutes les applications Windows utilisent cet appel pour créer des threads. Tous les langages évitent les appels d'API directs pour des raisons évidentes: 1. Vous ne voulez pas que votre code soit spécifique au système d'exploitation 2. Vous devez faire quelques tâches ménagères avant d'appeler API: convertir les paramètres et les résultats, allouer un stockage temporaire, etc._beginthreadex()
est un wrapper CCreateThread()
qui tient compte du C spécifique. Il permet aux C f-ns à thread unique d'origine de travailler dans un environnement multithread en allouant un stockage spécifique au thread.Si vous n'utilisez pas CRT, vous ne pouvez pas éviter un appel direct à
CreateThread()
. Si vous utilisez CRT, vous devez utiliser_beginthreadex()
ou certaines chaînes CRT f-ns peuvent ne pas fonctionner correctement avant VC2005.la source
CreateThread()
est l'appel système direct. Il est implémenté surKernel32.dll
lequel, très probablement, votre application sera déjà liée pour d'autres raisons. Il est toujours disponible dans les systèmes Windows modernes._beginthread()
et_beginthreadex()
sont des fonctions wrapper dans Microsoft C Runtime (msvcrt.dll
). Les différences entre les deux appels sont indiquées dans la documentation. Il est donc disponible lorsque le Microsoft C Runtime est disponible, ou si votre application est liée statiquement à celui-ci. Vous serez probablement lié à cette bibliothèque, à moins que vous ne codiez dans l'API Windows pure (comme je le fais souvent personnellement).Votre question est cohérente et en fait récurrente. Comme de nombreuses API, il existe des fonctionnalités dupliquées et ambiguës dans l'API Windows avec lesquelles nous devons faire face. Pire encore, la documentation ne clarifie pas le problème. Je suppose que la
_beginthread()
famille de fonctions a été créée pour une meilleure intégration avec d'autres fonctionnalités C standard, telles que la manipulation deerrno
._beginthread()
s'intègre ainsi mieux avec le runtime C.Malgré cela, à moins que vous n'ayez de bonnes raisons d'utiliser
_beginthread()
ou_beginthreadex()
, vous devriez utiliserCreateThread()
, principalement parce que vous pourriez avoir une dépendance de bibliothèque en moins dans votre exécutable final (et pour MS CRT, cela a un peu d'importance). Vous n'avez pas non plus de code d'encapsulation autour de l'appel, bien que cet effet soit négligeable. En d'autres termes, je pense que la principale raison de s'en tenirCreateThread()
est qu'il n'y a pas de bonne raison d'utiliser_beginthreadex()
pour commencer. Les fonctionnalités sont exactement, ou presque, les mêmes.Une bonne raison d'utiliser
_beginthread()
serait (car cela semble être faux) que les objets C ++ seraient correctement déroulés / détruits s'ils_endthread()
étaient appelés.la source
CreateThread
est l'appel de l'API Windows pour créer un thread. Si vous utilisez le CRT (parce que vous programmez en C ou C ++), vous devez créer des threads en utilisant les_beginthread[ex]
appels du CRT (qui appellentCreateThread
en plus d'effectuer l'initialisation CRT nécessaire). Différence la plus importante entre_beginthread
et l'ancienne variante: la première conserve la propriété du descripteur de thread natif, tandis que la seconde en transmet la propriété à l'appelant.msvcrt.dll
n'est pas la DLL d'exécution C! Voir blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273Les autres réponses n'abordent pas les implications de l'appel d'une fonction d'exécution C qui encapsule une fonction API Win32. Ceci est important lors de l'examen du comportement de verrouillage du chargeur DLL.
Que ce soit ou non
_beginthread{ex}
une gestion spéciale de la mémoire thread / fibre C Runtime comme le discutent les autres réponses, elle est implémentée dans (en supposant une liaison dynamique avec le run-time C) une DLL que les processus n'ont peut-être pas encore chargée.Il n'est pas sûr d'appeler
_beginthread*
depuisDllMain
. J'ai testé cela en écrivant une DLL chargée à l'aide de la fonction Windows "AppInit_DLLs". L'appel_beginthreadex (...)
au lieu deCreateThread (...)
fait que BEAUCOUP de parties importantes de Windows cessent de fonctionner pendant le démarrage, car lesDllMain
blocages de point d'entrée attendent que le verrou du chargeur soit libéré afin d'effectuer certaines tâches d'initialisation.Incidemment, c'est aussi pourquoi kernel32.dll a beaucoup de fonctions de chaîne qui se chevauchent que le run-time C fait également - utilisez celles de
DllMain
pour éviter le même genre de situation.la source
Si vous lisez le livre Debugging Windows Application From Jeffrey Richter, il explique que presque dans tous les cas, vous devez appeler
_beginthreadex
au lieu d'appelerCreateThread
._beginthread
est juste un wrapper simplifié_beginthreadex
._beginthreadex
initialise certains composants internes CRT (C RunTime) que l'CreateThread
API ne ferait pas.Une conséquence si vous utilisez l'
CreateThread
API au lieu d'utiliser des_begingthreadex
appels aux fonctions CRT peut provoquer des problèmes inattendus.Consultez cet ancien Microsoft Journal de Richter.
la source
Il n'y a plus de différence entre les deux.
Tous les commentaires sur les fuites de mémoire, etc. sont basés sur des versions <VS2005 très anciennes. J'ai fait des tests de résistance il y a des années et pourrais démystifier ce mythe. Même Microsoft mélange les styles dans leurs exemples, n'utilisant presque jamais _beginthread.
la source