¿Cuál es una mejor manera de comenzar un hilo _beginthread
, _beginthreadx
o CreateThread
?
Estoy tratando de determinar cuáles son las ventajas / desventajas de _beginthread
, _beginthreadex
y CreateThread
. Todas estas funciones devuelven un identificador de subproceso a un subproceso recién creado, ya sé que CreateThread proporciona un poco de información adicional cuando se produce un error (se puede verificar llamando GetLastError
) ... pero ¿cuáles son algunas cosas que debo considerar cuando ' Estoy usando estas funciones?
Estoy trabajando con una aplicación de Windows, por lo que la compatibilidad multiplataforma ya está fuera de discusión.
He revisado la documentación de msdn y simplemente no puedo entender, por ejemplo, por qué alguien decidiría usar _beginthread en lugar de CreateThread o viceversa.
¡Salud!
Actualización: OK, gracias por toda la información, también he leído en un par de lugares a los que no puedo llamar WaitForSingleObject()
si los usaba _beginthread()
, pero si llamo _endthread()
en el hilo, ¿no debería funcionar? ¿Cuál es el trato allí?
fuente
Respuestas:
CreateThread()
es una llamada API de Win32 sin procesar para crear otro subproceso de control a nivel del núcleo._beginthread()
&_beginthreadex()
son llamadas de biblioteca de tiempo de ejecución C que llamanCreateThread()
detrás de escena. Una vez queCreateThread()
ha regresado,_beginthread/ex()
se encarga de la contabilidad adicional para que la biblioteca de tiempo de ejecución C sea utilizable y coherente en el nuevo hilo.En C ++, seguramente debería usarlo a
_beginthreadex()
menos que no se vincule a la biblioteca de tiempo de ejecución de C (también conocido como MSVCRT * .dll / .lib).fuente
_begin
rutinas fueran llamadas internas yCreateThread
se suponía que era la función API que todos llamarían. Otra posible explicación es que la EM tiene una larga y gloriosa historia de ignorar el estándar y tomar decisiones muy malas sobre nombrar cosas._begin
funciones comienzan con un guión bajo porque Microsoft comenzó a seguir el estándar más de cerca. En el tiempo de ejecución de C, los nombres que están con guión bajo están reservados para la implementación (y la implementación puede documentarlos para el uso del usuario final, como con estos).beginthreadex()
es un nombre que el usuario puede usar. Si el tiempo de ejecución C lo usó, entonces podría entrar en conflicto con un símbolo de usuario final que el usuario tenía el derecho legítimo de poder esperar usar. Tenga en cuenta que las API de Win32 no forman parte del tiempo de ejecución de C y utilizan el espacio de nombres del usuario.CreateThread
y las llamadas CRT_beginthread/ex
, y al llamar al CRT en un hilo, que siempre deben ser creados con_beginthread/ex
. Es posible que ya no haya pérdidas de memoria, si no lo hace. Pero seguramente no obtendrá su entorno de punto flotante correctamente inicializado cuando llameCreateThread
, por ejemplo. Hay más : "Si un hilo creado usando CreateThread llama al CRT, el CRT puede terminar el proceso en condiciones de poca memoria".Hay varias diferencias entre
_beginthread()
y_beginthreadex()
._beginthreadex()
fue hecho para actuar más comoCreateThread()
(en ambos parámetros y cómo se comporta).Como Drew Hall menciona, si está utilizando el tiempo de ejecución C / C ++, debe usar
_beginthread()
/ en_beginthreadex()
lugar deCreateThread()
para que el tiempo de ejecución tenga la oportunidad de realizar su propia inicialización de subprocesos (configuración del almacenamiento local de subprocesos, etc.).En la práctica, esto significa que
CreateThread()
su código nunca debería ser usado directamente por su código.Los documentos de MSDN para
_beginthread()
/_beginthreadex()
tienen bastante detalle sobre las diferencias, una de las más importantes es que, dado que el identificador de hilo para un hilo creado por_beginthread()
se cierra automáticamente por el CRT cuando el hilo sale, "si el hilo generado por _beginthread sale rápidamente, el identificador devuelto al llamador de _beginthread podría no ser válido o, peor aún, señalar a otro hilo ".Esto es lo que dicen los comentarios
_beginthreadex()
en la fuente CRT:Actualización de enero de 2013:
El CRT para VS 2012 tiene un bit de inicialización adicional realizado
_beginthreadex()
: si el proceso es una "aplicación empaquetada" (si se devuelve algo útilGetCurrentPackageId()
), el tiempo de ejecución inicializará el MTA en el subproceso recién creado.fuente
CreateThread
ser lo correcto son cada vez más escasas.En general, lo correcto es llamar
_beginthread()/_endthread()
(o lasex()
variantes). Sin embargo, si se utiliza el CRT como .dll, el estado CRT se ha inicializado correctamente y destruido como el CRTDllMain
se llamará conDLL_THREAD_ATTACH
yDLL_THREAD_DETACH
al llamarCreateThread()
yExitThread()
o regresar, respectivamente.El
DllMain
código para el CRT se puede encontrar en el directorio de instalación de VS en VC \ crt \ src \ crtlib.c.fuente
Este es el código en el núcleo de
_beginthreadex
(vercrt\src\threadex.c
):El resto de
_beginthreadex
inicializa la estructura de datos por hilo para CRT.La ventaja de usar
_beginthread*
es que sus llamadas CRT desde el hilo funcionarán correctamente.fuente
Debería usar
_beginthread
o_beginthreadex
permitir que la biblioteca de tiempo de ejecución C haga su propia inicialización del hilo. Solo los programadores de C / C ++ necesitan saber esto, ya que ahora deberían conocer las reglas de uso de su propio entorno de desarrollo.Si lo usa
_beginthread
, no necesita llamar,CloseHandle
ya que RTL lo hará por usted. Es por eso que no puede esperar en el mango si lo ha usado_beginthread
. También_beginthread
conduce a la confusión si la función de subproceso sale inmediatamente (rápidamente) ya que el subproceso de inicio puede quedar sosteniendo un identificador de subproceso no válido para el subproceso que acaba de iniciar._beginthreadex
los identificadores se pueden usar para esperar pero también requieren una llamada explícita aCloseHandle
. Esto es parte de lo que los hace seguros para usar con wait. Otro problema para que sea completamente infalible es siempre iniciar el hilo suspendido. Verifique el éxito, identificador de registro, etc. El hilo de reanudar. Esto es necesario para evitar que un subproceso termine antes de que el subproceso de inicio pueda grabar su identificador.La mejor práctica es usar
_beginthreadex
, comenzar a suspender y luego reanudar después de grabar el identificador, esperar en el identificador está bien,CloseHandle
debe llamarsefuente
CreateThread()
solía tener pérdidas de memoria cuando utiliza funciones CRT en su código._beginthreadex()
tiene los mismos parámetros queCreateThread()
y es más versátil que_beginthread()
. Así que te recomiendo que uses_beginthreadex()
.fuente
CreateThread
no nunca perder memoria. Es más bien el CRT el que lo hace, cuando se llama desde un hilo que no se ha inicializado correctamente.Con respecto a su pregunta actualizada: "También he leído en un par de lugares a los que no puedo llamar
WaitForSingleObject()
si los usaba_beginthread()
, pero si llamo_endthread()
en el hilo, ¿no debería funcionar?"En general, puede pasar un identificador de subproceso a
WaitForSingleObject()
(u otras API que esperan en los identificadores de objeto) para bloquear hasta que el subproceso se haya completado. Pero el identificador de subproceso creado por_beginthread()
se cierra cuando_endthread()
se llama (lo que se puede hacer explícitamente o implícitamente por el tiempo de ejecución cuando vuelve el procedimiento de subproceso).El problema se menciona en la documentación para
WaitForSingleObject()
:fuente
En cuanto a las firmas de funciones,
CreateThread
es casi idéntico a_beginthreadex
._beginthread
,_beginthreadx
vsCreateThread
Los comentarios aquí indicados
_beginthread
pueden usar una convención__cdecl
o una__clrcall
llamada como punto de inicio, y_beginthreadex
pueden usar cualquiera__stdcall
o__clrcall
para el punto de inicio.Creo que cualquier comentario que la gente haya hecho sobre las pérdidas de memoria
CreateThread
tiene más de una década y probablemente debería ignorarse.Curiosamente, ambas
_beginthread*
funciones en realidad llamanCreateThread
bajo el capó, enC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
mi máquina.fuente
beginthreadex
te da un hiloHANDLE
para usar enWaitForSingleObject
y amigos.beginthread
no lo hace No olvidesCloseHandle()
cuando hayas terminado. La respuesta real sería usarboost::thread
o pronto la clase de hilo de C ++ 09.fuente
En comparación con
_beginthread
, con_beginthreadex
usted puede:OpenThread
.CloseHandle
.El
_beginthreadex
se parece muchoCreateThread
, pero el primero es una implementación de CRT y el segundo una llamada a la API de Windows. La documentación para CreateThread contiene la siguiente recomendación:fuente
_beginthreadex
. Puede emitir eluintptr_t
retorno de ambas funciones aHANDLE
._beginthread
cierra el tirador al salir. Por lo tanto, no puede usar el identificador de manera confiable con las API de sincronización o para obtener la identificación del subproceso hasta y a menos que use otra forma de sincronizar y duplicar el identificador. Pero entonces lo está_beginthreadex
haciendo por ti.CreateThread()
una vez fue un no-no porque el CRT se inicializaría / limpiaría incorrectamente. Pero esto ahora es historia: ahora se puede (usando VS2010 y probablemente algunas versiones anteriores) llamarCreateThread()
sin romper el CRT.Aquí está la confirmación oficial de MS . Establece una excepción:
Sin embargo, desde el punto de vista de la consistencia, personalmente prefiero seguir usando
_beginthreadex()
.fuente
CreateThread()
es la llamada a la API de Windows que es neutral al lenguaje. Simplemente crea un objeto OS - thread y devuelve HANDLE a este thread. Todas las aplicaciones de Windows están utilizando esta llamada para crear hilos. Todos los idiomas evitan las llamadas directas a la API por razones obvias: 1. No desea que su código sea específico del sistema operativo 2. Debe realizar algunas tareas de mantenimiento antes de llamar a API: convertir parámetros y resultados, asignar almacenamiento temporal, etc._beginthreadex()
es la envoltura de CCreateThread()
que da cuenta de C específica. Permite que los F-ns de un solo subproceso original funcionen en un entorno multiproceso al asignar almacenamiento específico de subproceso.Si no usa CRT, no puede evitar una llamada directa
CreateThread()
. Si usa CRT, debe usar_beginthreadex()
o algunas cadenas de CRT f-ns pueden no funcionar correctamente antes de VC2005.fuente
CreateThread()
es la llamada directa al sistema. Está implementado en elKernel32.dll
cual, muy probablemente, su aplicación ya estará vinculada por otras razones. Siempre está disponible en los sistemas modernos de Windows._beginthread()
y_beginthreadex()
son funciones de contenedor en Microsoft C Runtime (msvcrt.dll
). Las diferencias entre las dos llamadas se indican en la documentación. Por lo tanto, está disponible cuando Microsoft C Runtime está disponible, o si su aplicación está vinculada estáticamente con ella. Es probable que también se vincule con esa biblioteca, a menos que esté codificando en la API de Windows pura (como lo hago personalmente a menudo).Su pregunta es coherente y en realidad recurrente. Como muchas API, hay una funcionalidad duplicada y ambigua en la API de Windows con la que tenemos que lidiar. Lo peor de todo, la documentación no aclara el problema. Supongo que la
_beginthread()
familia de funciones se creó para una mejor integración con otras funcionalidades C estándar, como la manipulación deerrno
._beginthread()
así se integra mejor con el tiempo de ejecución de C.A pesar de eso, a menos que tenga buenas razones para usar
_beginthread()
o_beginthreadex()
, debe usarloCreateThread()
, principalmente porque podría obtener una dependencia de biblioteca menos en su ejecutable final (y para MS CRT esto importa un poco). Tampoco tiene un código de ajuste alrededor de la llamada, aunque este efecto es insignificante. En otras palabras, creo que la razón principal para seguirCreateThread()
es que no hay una buena razón_beginthreadex()
para comenzar. Las funcionalidades son precisamente, o casi, las mismas.Una buena razón para usar
_beginthread()
sería (ya que parece ser falso) que los objetos C ++ se desenrollarían / destruirían adecuadamente si_endthread()
se llamara.fuente
CreateThread
es la llamada a la API de Windows para crear un hilo. Si está utilizando el CRT (porque está programando en C o C ++), debe crear hilos utilizando las_beginthread[ex]
llamadas del CRT (que llamanCreateThread
además de realizar la inicialización del CRT necesaria). La diferencia más importante entre_beginthread
y la ex-variante: el primero conserva la propiedad del identificador de subproceso nativo, mientras que el segundo pasa la propiedad a la persona que llama.msvcrt.dll
es la DLL de tiempo de ejecución C! Ver blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273Las otras respuestas no discuten las implicaciones de llamar a una función de tiempo de ejecución C que envuelve una función API Win32. Esto es importante cuando se considera el comportamiento de bloqueo del cargador de DLL.
Ya sea que se
_beginthread{ex}
administre o no una gestión especial de memoria de fibra / hilo de tiempo de ejecución de C, como se analiza en las otras respuestas, se implementa en (suponiendo un enlace dinámico al tiempo de ejecución de C) una DLL que los procesos pueden no haberse cargado todavía.No es seguro llamar
_beginthread*
desdeDllMain
. He probado esto escribiendo una DLL cargada usando la función "AppInit_DLLs" de Windows. Llamar en_beginthreadex (...)
lugar deCreateThread (...)
provocar que MUCHAS partes importantes de Windows dejen de funcionar durante el arranque como losDllMain
puntos muertos de punto de entrada que esperan que se libere el bloqueo del cargador para realizar ciertas tareas de inicialización.Por cierto, esta es también la razón por la que kernel32.dll tiene muchas funciones de cadena superpuestas que también tiene el tiempo de ejecución de C: úselas
DllMain
para evitar el mismo tipo de situación.fuente
Si lees el libro Depuración de aplicaciones de Windows de Jeffrey Richter, explica que casi en todos los casos debes llamar en
_beginthreadex
lugar de llamarCreateThread
._beginthread
es solo un contenedor simplificado_beginthreadex
._beginthreadex
inicializa ciertas partes internas de CRT (C RunTime) que laCreateThread
API no haría.Una consecuencia si usa la
CreateThread
API en lugar de usar_begingthreadex
llamadas a funciones CRT podría causar problemas inesperados.Echa un vistazo a este viejo Microsoft Journal de Richter.
fuente
Ya no hay diferencia entre los dos.
Todos los comentarios sobre pérdidas de memoria, etc. se basan en versiones muy antiguas de <VS2005. He hecho algunas pruebas de estrés hace años y podría desacreditar este mito. Incluso Microsoft mezcla los estilos en sus ejemplos, casi nunca usa _beginthread.
fuente