c++ - create thread
Enhebrado de Windows:_beginthread vs_beginthreadex vs CreateThread C++ (16)
Comparado con _beginthread
, con _beginthreadex
puedes:
- Especificar atributos de seguridad.
- Comience un hilo en estado suspendido.
- Puede obtener el id. De subproceso que se puede usar con
OpenThread
. - Se garantiza que el identificador de subproceso devuelto sea válido si la llamada fue exitosa. Allí debe cerrar el identificador con
CloseHandle
. - El identificador de subproceso devuelto se puede usar con API de sincronización.
El _beginthreadex
se asemeja mucho a CreateThread
, pero el primero es una implementación de CRT y la segunda una llamada a la API de Windows. La documentación de CreateThread contiene la siguiente recomendación:
Un hilo en un ejecutable que llama a la biblioteca de tiempo de ejecución de C (CRT) debe usar las funciones
_beginthreadex
y_endthreadex
para la gestión de subprocesos en lugar deCreateThread
yExitThread
; esto requiere el uso de la versión multiproceso del CRT. Si un hilo creado utilizandoCreateThread
llama al CRT, el CRT puede finalizar el proceso en condiciones de poca memoria.
¿Cuál es una mejor manera de iniciar 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 comprobar llamando a GetLastError
) ... pero ¿cuáles son algunas cosas que debería considerar cuando estoy usando estas funciones?
Estoy trabajando con una aplicación de Windows, por lo que la compatibilidad multiplataforma ya está descartada.
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.
¡Aclamaciones!
Actualización: OK, gracias por toda la información, también he leído en un par de lugares que no puedo llamar a WaitForSingleObject()
si usé _beginthread()
, pero si llamo a _endthread()
en el hilo no debería ¿trabajo? ¿Cuál es el trato allí?
Con respecto a su pregunta actualizada: "También he leído en un par de lugares que no puedo llamar a WaitForSingleObject()
si utilizo _beginthread()
, pero si llamo a _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 se complete el subproceso. Pero el identificador de subproceso creado por _beginthread()
se cierra cuando se llama a _endthread()
(lo cual se puede hacer explícitamente o se realiza implícitamente por el tiempo de ejecución cuando se devuelve el procedimiento de subproceso).
El problema se menciona en la documentación de WaitForSingleObject()
:
Si este identificador se cierra mientras la espera aún está pendiente, el comportamiento de la función no está definido.
Debe usar _beginthread
o _beginthreadex
para permitir que la biblioteca C runtime realice su propia inicialización del hilo. Solo los programadores C / C ++ deben saber esto como deberían hacerlo ahora, las reglas de uso de su propio entorno de desarrollo.
Si usa _beginthread
no necesita llamar a CloseHandle
ya que RTL lo hará por usted. Es por eso que no puede esperar en el mango si ha usado _beginthread
. También _beginthread
lleva a confusión si la función de subproceso sale inmediatamente (rápidamente) ya que el subproceso de lanzamiento puede quedar con un identificador de subproceso no válido al subproceso que acaba de iniciar.
_beginthreadex
manejadores _beginthreadex
se pueden usar para esperar pero también requieren una llamada explícita a CloseHandle
. Esto es parte de lo que los hace seguros para usar con la espera. Hay otro problema para hacerlo totalmente infalible es siempre iniciar el hilo suspendido. Verifique el éxito, el identificador de registro, etc. El hilo de reanudar. Esto es necesario para evitar que un hilo termine antes de que el hilo de inicio pueda grabar su identificador.
La mejor práctica es usar _beginthreadex
, iniciar suspendido y luego reanudar después de registrar el identificador, esperar en el manejador es correcto, se debe llamar a CloseHandle
.
En cuanto a las firmas de funciones, CreateThread
es casi idéntico a _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
);
Las observaciones de aquí dicen _beginthread
puede usar __cdecl
o __clrcall
llamando a la convención como punto de inicio, y _beginthreadex
puede usar __stdcall
o __clrcall
para el punto de inicio.
Creo que todos los comentarios que las personas hicieron sobre fugas de memoria en CreateThread
tienen más de una década y probablemente deberían ignorarse.
Curiosamente, ambas funciones _beginthread*
realmente llaman CreateThread
bajo el capó, en C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/crt/src
en mi máquina.
// 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;
}
En general, lo correcto es llamar a _beginthread()/_endthread()
(o las variantes ex()
). Sin embargo, si utiliza el CRT como .dll, el estado del CRT se inicializará y destruirá de manera adecuada, ya que el DllMain
del CRT se DllMain
con DLL_THREAD_ATTACH
y DLL_THREAD_DETACH
al llamar a CreateThread()
y ExitThread()
o al regresar, respectivamente.
El código DllMain
para CRT se puede encontrar en el directorio de instalación de VS en VC / crt / src / crtlib.c.
Este es el código en el núcleo de _beginthreadex
(vea 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;
}
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.
Hay varias diferencias entre _beginthread()
y _beginthreadex()
. _beginthreadex()
se hizo para actuar más como CreateThread()
(en ambos parámetros y cómo se comporta).
Como menciona Drew Hall , si está utilizando el tiempo de ejecución de C / C ++, debe usar _beginthread()
/ _beginthreadex()
lugar de CreateThread()
para que el tiempo de ejecución tenga la oportunidad de realizar su propia inicialización de subprocesos (configuración de almacenamiento local de subprocesos) , etc.)
En la práctica, esto significa que CreateThread()
casi nunca debe ser usado directamente por su código.
Los documentos de MSDN para _beginthread()
/ _beginthreadex()
tienen bastante detalle sobre las diferencias, uno de los más importantes es que el CRT cierra automáticamente el identificador de subproceso creado por _beginthread()
cuando sale el hilo , "si el hilo generado por _beginthread sale rápidamente, el identificador devuelto a la persona que llama de _beginthread podría no ser válido o, lo que es peor, apuntar a otro hilo".
Aquí está lo que los comentarios para _beginthreadex()
en la fuente de CRT tienen que decir:
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).
Actualización de enero de 2013:
El CRT para VS 2012 tiene un bit de inicialización adicional realizado en _beginthreadex()
: si el proceso es una "aplicación empaquetada" (si se devuelve algo útil de GetCurrentPackageId()
) el tiempo de ejecución inicializará el MTA en el subproceso recién creado.
Las otras respuestas no discuten las implicaciones de llamar a una función de tiempo de ejecución de C que envuelve una función de API de Win32. Esto es importante cuando se considera el comportamiento de bloqueo del cargador DLL.
Si _beginthread{ex}
realiza alguna gestión especial de la memoria de fibra / hilo Runtime como se _beginthread{ex}
en las otras respuestas, se implementa en (suponiendo un enlace dinámico al tiempo de ejecución de C) una DLL que los procesos aún no se han cargado.
No es seguro llamar a _beginthread*
desde DllMain
. He probado esto escribiendo una DLL cargada usando la función "AppInit_DLLs" de Windows. Llamar a _beginthreadex (...)
lugar de CreateThread (...)
hace que CreateThread (...)
partes importantes de Windows dejen de funcionar durante el arranque como los puntos muertos de punto de entrada de DllMain
esperando a que se libere el bloqueo del cargador para realizar ciertas tareas de inicialización .
Dicho sea de paso, esta es la razón por la que kernel32.dll tiene muchas funciones de cadena superpuestas que el tiempo de ejecución de C también hace: use las de DllMain
para evitar el mismo tipo de situación.
Si lees el libro Debugging Windows Application de Jeffrey Richter, explica que casi en todos los casos debes llamar a _beginthreadex
lugar de llamar a CreateThread
. _beginthread
es solo un contenedor simplificado alrededor de _beginthreadex
.
_beginthreadex
inicializa ciertos componentes internos de CRT (C RunTime) que la API CreateThread
no haría.
Una consecuencia si usas la API CreateThread
lugar de usar _begingthreadex
llamadas a funciones CRT pueden causar problemas inesperados.
Ya no hay diferencia entre los dos.
Todos los comentarios sobre fugas de memoria, etc. se basan en versiones <VS2005 muy antiguas. 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 usando _beginthread.
microsoft.com/msj/0799/win32/win320799.aspx cuando usa funciones CRT en su código. _beginthreadex()
tiene los mismos parámetros que CreateThread()
y es más versátil que _beginthread()
. Así que te recomiendo que uses _beginthreadex()
.
CreateThread()
es la llamada a la API de Windows que es de idioma neutral. Simplemente crea el objeto OS - thread y devuelve HANDLE a este hilo. Todas las aplicaciones de Windows están usando esta llamada para crear subprocesos. Todos los idiomas evitan la llamada directa 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 al tipo API: convertir parámetros y resultados, asignar almacenamiento temporal, etc.
_beginthreadex()
es C wrapper alrededor de CreateThread()
que cuenta para C específico. Permite que los Cf-ns originales de un solo hilo funcionen en entornos multiproceso mediante la asignación de almacenamiento específico de subprocesos.
Si no usa CRT, no puede evitar una llamada directa a CreateThread()
. Si usa CRT, debe usar _beginthreadex()
o algunas cadenas de caracteres CRT pueden no funcionar correctamente antes de VC2005.
CreateThread()
es la llamada directa al sistema. Está implementado en Kernel32.dll
que, probablemente, su aplicación ya estará vinculada por otros motivos. Siempre está disponible en los sistemas modernos de Windows.
_beginthread()
y _beginthreadex()
son funciones contenedoras en Microsoft C Runtime ( msvcrt.dll
). Las diferencias entre las dos llamadas se establecen en la documentación. Por lo tanto, está disponible cuando Microsoft C Runtime está disponible o si su aplicación está vinculada de forma estática. Es probable que se vincule con esa biblioteca también, a menos que esté codificando en API pura de Windows (como personalmente lo hago a menudo).
Tu 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 es que la documentación no aclara el problema. Supongo que la familia de funciones _beginthread()
se creó para una mejor integración con otras funcionalidades C estándar, como la manipulación de errno
. _beginthread()
se integra mejor con el tiempo de ejecución C.
A pesar de eso, a menos que tenga buenas razones para usar _beginthread()
o _beginthreadex()
, debería usar CreateThread()
, principalmente porque podría obtener una dependencia de biblioteca menos en su ejecutable final (y para MS CRT esto sí 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 seguir con CreateThread()
es que no hay una buena razón para usar _beginthreadex()
para empezar. Las funcionalidades son precisamente, o casi, las mismas.
Una buena razón para usar _beginthread()
sería (como parece ser falso) que los objetos de C ++ se deshicieran / destruyeran correctamente si se _endthread()
.
CreateThread()
es una llamada cruda a la API de Win32 para crear otro hilo de control a nivel del kernel.
_beginthread()
y _beginthreadex()
son llamadas de la biblioteca C runtime que llaman a CreateThread()
detrás de las escenas. Una vez que CreateThread()
ha regresado, _beginthread/ex()
se encarga de la contabilidad adicional para que la biblioteca de tiempo de ejecución de C sea utilizable y coherente en el nuevo hilo.
En C ++ casi seguramente _beginthreadex()
usar _beginthreadex()
menos que no estés enlazando a la biblioteca C runtime en absoluto (también conocido como MSVCRT * .dll / .lib).
CreateThread()
una vez fue un no-no porque el CRT se inicializaría / limpiaría incorrectamente. Pero ahora esto es historia: ahora se puede (utilizando VS2010 y probablemente algunas versiones atrás) llamar a CreateThread()
sin romper el CRT.
Aquí está la confirmación oficial de MS . Establece una excepción:
En realidad, la única función que no se debe usar en un hilo creado con
CreateThread()
es la funciónsignal()
.
Sin embargo, desde el punto de vista de la consistencia, personalmente prefiero seguir usando _beginthreadex()
.
beginthreadex
le proporciona un HANDLE
hilo para utilizar en WaitForSingleObject
y sus amigos. beginthread
no. No te olvides de CloseHandle()
cuando hayas terminado. La respuesta real sería usar boost::thread
o pronto la clase de subprocesos de C ++ 09.