Compartir a través de


_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, Securityy threadaddr. El nuevo subproceso se puede crear en un estado suspendido, con una seguridad especificada, y se puede acceder a él por medio de thrdaddr, 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 _beginthreadexla 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, _exito 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 = &param;

    // 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, &region);

        // 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 _endthreadexa , 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

Consulte también