_beginthread
, _beginthreadex
Crea un subproceso.
Sintaxis
uintptr_t _beginthread( // NATIVE CODE
void( __cdecl *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthread( // MANAGED CODE
void( __clrcall *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
uintptr_t _beginthreadex( // MANAGED CODE
void *security,
unsigned stack_size,
unsigned ( __clrcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
Parámetros
start_address
Dirección de inicio de una rutina que comienza la ejecución de un nuevo subproceso. Para _beginthread
, la convención de llamada es __cdecl
(para código nativo) o __clrcall
(para código administrado). Para _beginthreadex
, la convención de llamada es __stdcall
(para código nativo) o __clrcall
(para código administrado).
stack_size
Tamaño de la pila de un subproceso nuevo, o 0.
arglist
Lista de argumentos que se van a pasar a un subproceso nuevo, o NULL
.
Security
Puntero a una estructura SECURITY_ATTRIBUTES
que determina si el identificador devuelto se puede heredar de procesos secundarios. Si Security
es NULL
, el identificador no se puede heredar.
initflag
Marcas que controlan el estado inicial de un nuevo subproceso. Establezca initflag
en 0 para que se ejecute de inmediato o en CREATE_SUSPENDED
para crear el subproceso en un estado suspendido; use ResumeThread
para ejecutar el subproceso. Establézcalo initflag
STACK_SIZE_PARAM_IS_A_RESERVATION
como marca que se usará stack_size
como tamaño de reserva inicial de la pila en bytes; si no se especifica esta marca, stack_size
especifica el tamaño de confirmación.
thrdaddr
Señala a una variable de 32 bits que recibe el identificador del subproceso. Si es NULL
, no se usa.
Valor devuelto
Si es correcto, cada una de estas funciones devuelve un identificador al subproceso creado recientemente; sin embargo, si el subproceso recién creado sale demasiado rápido, _beginthread
no puede devolver un identificador válido. (Consulte el análisis en la sección Comentarios). En un error, _beginthread
devuelve -1L y errno
se establece en EAGAIN
si hay demasiados subprocesos, en EINVAL
si el argumento no es válido o el tamaño de pila es incorrecto, o en EACCES
si hay recursos insuficientes (como memoria). En un error, _beginthreadex
devuelve 0 y se establecen errno
y _doserrno
.
Si start_address
es NULL
, se invoca el controlador de parámetros no válidos, tal y como se describe en Validación de parámetros. Si la ejecución puede continuar, estas funciones establecen errno
en EINVAL
y devuelven -1.
Para obtener más información sobre estos y otros códigos de retorno, vea errno
, _doserrno
, _sys_errlist
y _sys_nerr
.
Para obtener más información sobre uintptr_t
, vea Tipos estándar.
Comentarios
La función de _beginthread
crea un subproceso que comienza la ejecución de una rutina en start_address
. La rutina en start_address
debe utilizar la convención de llamada __cdecl
(para código nativo) o __clrcall
(para código administrado) y no debe tener ningún valor devuelto. Cuando el subproceso vuelve de esa rutina, se finaliza automáticamente. Para obtener más información sobre los subprocesos, vea Compatibilidad con multithreading para código anterior (Visual C++).
_beginthreadex
se parece a la API CreateThread
de Win32 más que _beginthread
. _beginthreadex
difiere de _beginthread
en lo siguiente:
_beginthreadex
tiene tres parámetros más:initflag
,Security
ythreadaddr
. El nuevo subproceso se puede crear en un estado suspendido, con una seguridad especificada, y se puede acceder a él por medio dethrdaddr
, que es el identificador del subproceso.La rutina en
start_address
que se pasa a_beginthreadex
debe utilizar la convención de llamada__stdcall
(para código nativo) o__clrcall
(para código administrado) y debe devolver un código de salida del subproceso._beginthreadex
devuelve 0 en el error, en lugar de -1L.Un subproceso que se crea mediante
_beginthreadex
termina con una llamada a_endthreadex
.
La función de _beginthreadex
le ofrece más control sobre cómo se crea el subproceso que hace _beginthread
. La función de _endthreadex
también es más flexible. Por ejemplo, con _beginthreadex
, puede usar la información de seguridad, establecer el estado inicial del subproceso (en ejecución o en suspensión) y obtener el identificador del subproceso del subproceso creado recientemente. También puede usar el identificador de subproceso devuelto por _beginthreadex
con las API de sincronización, que no puede hacer con _beginthread
.
_beginthreadex
es más seguro usar que _beginthread
. Si el subproceso que genera _beginthread
sale rápidamente, el identificador que se devuelve al llamador de _beginthread
podría ser no válido o señalar a otro subproceso. Sin embargo, el autor de _beginthreadex
la llamada de tiene que cerrar el identificador devuelto por _beginthreadex
, por lo que se garantiza que sea un identificador válido si _beginthreadex
no devolvió un error.
Puede llamar a _endthread
o _endthreadex
explícitamente para finalizar un subproceso; sin embargo, se llama a _endthread
o _endthreadex
automáticamente cuando el subproceso vuelve de la rutina que se pasa como parámetro. Si se finaliza un subproceso con una llamada a _endthread
o las ayudas de _endthreadex
, se garantiza la recuperación correcta de los recursos que se asignan para el subproceso.
_endthread
cierra automáticamente el identificador del subproceso, mientras _endthreadex
que no lo hace. Por lo tanto, cuando use _beginthread
y _endthread
, no cierre explícitamente el identificador de subproceso llamando a la API de Win32 CloseHandle
. Este comportamiento es distinto que el de la API ExitThread
de Win32.
Nota:
Para un archivo ejecutable vinculado con Libcmt.lib, no llame a la API ExitThread
Win32 a fin de no impedir que el sistema en tiempo de ejecución reclame los recursos asignados. _endthread
y _endthreadex
recuperan los recursos de subprocesos asignados y después llaman a ExitThread
.
El sistema operativo controla la asignación de la pila cuando se llama a _beginthread
o _beginthreadex
; no tiene que pasar la dirección de la pila del subproceso a ninguna de estas funciones. Además, el argumento de stack_size
puede ser 0, en cuyo caso el sistema operativo utiliza el mismo valor que la pila que se especifica para el subproceso principal.
arglist
es un parámetro que se pasará al subproceso recién creado. Normalmente, es la dirección de un elemento de datos, como una cadena de caracteres. arglist
puede ser NULL
si no es necesario, pero _beginthread
y _beginthreadex
se debe proporcionar algún valor para pasar al nuevo subproceso. Se terminan todos los subprocesos si cualquier subproceso llama a abort
, exit
, _exit
o ExitProcess
.
La configuración regional del subproceso nuevo se inicializa mediante la información de configuración regional actual global para cada proceso. Si la configuración regional para cada subproceso está habilitada mediante una llamada a _configthreadlocale
(tanto globalmente como solo para subprocesos), el subproceso puede cambiar su configuración regional independientemente de otros subprocesos con una llamada a setlocale
o _wsetlocale
. Los subprocesos que no tienen establecida la marca de configuración regional por subproceso pueden afectar a la información de configuración regional en todos los demás subprocesos que tampoco tienen establecida la marca de configuración regional por subproceso y también todos los subprocesos recién creados. Para obtener más información, vea Locale.
Para el código /clr
, _beginthread
y _beginthreadex
tienen dos sobrecargas cada uno. Una toma un puntero de función de convención nativa de llamadas, mientras que la otra toma un puntero de función __clrcall
. La primera sobrecarga no es segura para el dominio de la aplicación y nunca lo será. Si está escribiendo /clr
código, debe asegurarse de que el nuevo subproceso entra en el dominio de aplicación correcto antes de acceder a los recursos administrados. Puede hacerlo, por ejemplo, mediante call_in_appdomain
. La segunda sobrecarga es una aplicación de dominio seguro; el subproceso recién creado finalizará siempre en el dominio de aplicación del llamador de _beginthread
o de _beginthreadex
.
De manera predeterminada, el estado global de esta función está limitado a la aplicación. Para cambiar este comportamiento, consulte Estado global en CRT.
Requisitos
Routine | Encabezado necesario |
---|---|
_beginthread |
<process.h> |
_beginthreadex |
<process.h> |
Para obtener más información sobre compatibilidad, consulte Compatibilidad.
Bibliotecas
Solo las versiones de multiproceso de las bibliotecas en tiempo de ejecución de C .
Para utilizar _beginthread
o _beginthreadex
, la aplicación debe vincularse a una de las bibliotecas en tiempo de ejecución multiproceso de C.
Ejemplos
En el ejemplo siguiente se utiliza _beginthread
y _endthread
.
// crt_BEGTHRD.C
// compile with: /MT /D "_X86_" /c
// processor: x86
#include <windows.h>
#include <process.h> /* _beginthread, _endthread */
#include <stddef.h>
#include <stdlib.h>
#include <conio.h>
void Bounce( void * );
void CheckKey( void * );
// GetRandom returns a random integer between min and max.
#define GetRandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))
// GetGlyph returns a printable ASCII character value
#define GetGlyph( val ) ((char)((val + 32) % 93 + 33))
BOOL repeat = TRUE; // Global repeat flag
HANDLE hStdOut; // Handle for console window
CONSOLE_SCREEN_BUFFER_INFO csbi; // Console information structure
int main()
{
int param = 0;
int * pparam = ¶m;
// Get display screen's text row and column information.
hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
GetConsoleScreenBufferInfo( hStdOut, &csbi );
// Launch CheckKey thread to check for terminating keystroke.
_beginthread( CheckKey, 0, NULL );
// Loop until CheckKey terminates program or 1000 threads created.
while( repeat && param < 1000 )
{
// launch another character thread.
_beginthread( Bounce, 0, (void *) pparam );
// increment the thread parameter
param++;
// Wait one second between loops.
Sleep( 1000L );
}
}
// CheckKey - Thread to wait for a keystroke, then clear repeat flag.
void CheckKey( void * ignored )
{
_getch();
repeat = 0; // _endthread implied
}
// Bounce - Thread to create and control a colored letter that moves
// around on the screen.
//
// Params: parg - the value to create the character from
void Bounce( void * parg )
{
char blankcell = 0x20;
CHAR_INFO ci;
COORD oldcoord, cellsize, origin;
DWORD result;
SMALL_RECT region;
cellsize.X = cellsize.Y = 1;
origin.X = origin.Y = 0;
// Generate location, letter and color attribute from thread argument.
srand( _threadid );
oldcoord.X = region.Left = region.Right =
GetRandom(csbi.srWindow.Left, csbi.srWindow.Right - 1);
oldcoord.Y = region.Top = region.Bottom =
GetRandom(csbi.srWindow.Top, csbi.srWindow.Bottom - 1);
ci.Char.AsciiChar = GetGlyph(*((int *)parg));
ci.Attributes = GetRandom(1, 15);
while (repeat)
{
// Pause between loops.
Sleep( 100L );
// Blank out our old position on the screen, and draw new letter.
WriteConsoleOutputCharacterA(hStdOut, &blankcell, 1, oldcoord, &result);
WriteConsoleOutputA(hStdOut, &ci, cellsize, origin, ®ion);
// Increment the coordinate for next placement of the block.
oldcoord.X = region.Left;
oldcoord.Y = region.Top;
region.Left = region.Right += GetRandom(-1, 1);
region.Top = region.Bottom += GetRandom(-1, 1);
// Correct placement (and beep) if about to go off the screen.
if (region.Left < csbi.srWindow.Left)
region.Left = region.Right = csbi.srWindow.Left + 1;
else if (region.Right >= csbi.srWindow.Right)
region.Left = region.Right = csbi.srWindow.Right - 2;
else if (region.Top < csbi.srWindow.Top)
region.Top = region.Bottom = csbi.srWindow.Top + 1;
else if (region.Bottom >= csbi.srWindow.Bottom)
region.Top = region.Bottom = csbi.srWindow.Bottom - 2;
// If not at a screen border, continue, otherwise beep.
else
continue;
Beep((ci.Char.AsciiChar - 'A') * 100, 175);
}
// _endthread given to terminate
_endthread();
}
Presione cualquier tecla para finalizar la aplicación de ejemplo.
En el ejemplo de código siguiente, se muestra cómo se puede usar el identificador de subproceso devuelto por _beginthreadex
con la API de sincronización WaitForSingleObject
. El subproceso principal espera que el segundo subproceso finalice antes de continuar. Cuando el segundo subproceso llama _endthreadex
a , hace que su objeto de subproceso vaya al estado señalado, lo que permite que el subproceso principal continúe ejecutándose. No se puede hacer con _beginthread
y _endthread
, porque _endthread
llama a CloseHandle
, que destruye el objeto de subproceso antes de que se pueda establecer en el estado señalado.
// crt_begthrdex.cpp
// compile with: /MT
#include <windows.h>
#include <stdio.h>
#include <process.h>
unsigned Counter;
unsigned __stdcall SecondThreadFunc( void* pArguments )
{
printf( "In second thread...\n" );
while ( Counter < 1000000 )
Counter++;
_endthreadex( 0 );
return 0;
}
int main()
{
HANDLE hThread;
unsigned threadID;
printf( "Creating second thread...\n" );
// Create the second thread.
hThread = (HANDLE)_beginthreadex( NULL, 0, &SecondThreadFunc, NULL, 0, &threadID );
// Wait until second thread terminates. If you comment out the line
// below, Counter will not be correct because the thread has not
// terminated, and Counter most likely has not been incremented to
// 1000000 yet.
WaitForSingleObject( hThread, INFINITE );
printf( "Counter should be 1000000; it is-> %d\n", Counter );
// Destroy the thread object.
CloseHandle( hThread );
}
Creating second thread...
In second thread...
Counter should be 1000000; it is-> 1000000