Subprocesamiento múltiple y configuraciones regionales

Tanto la biblioteca en tiempo de ejecución de C como la biblioteca estándar de C++ proporcionan compatibilidad para cambiar la configuración regional del programa. En este tema se abordan los problemas que surgen al usar la función de configuración regional de ambas bibliotecas en una aplicación multiproceso.

Comentarios

Con la biblioteca en tiempo de ejecución de C, puede crear aplicaciones multiproceso mediante las funciones _beginthread y _beginthreadex. En este tema solo se tratan las aplicaciones multiproceso creadas con estas funciones. Para obtener más información, vea _beginthread, _beginthreadex.

Para cambiar la configuración regional mediante la biblioteca en tiempo de ejecución de C, use la función setlocale. En versiones anteriores de Visual C++, esta función siempre modificaba la configuración regional en toda la aplicación. Ahora hay compatibilidad para establecer la configuración regional por subproceso. Esto se hace con la función _configthreadlocale. Para especificar que setlocale solo debe cambiar la configuración regional en el subproceso actual, llame a _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) en ese subproceso. Por el contrario, la llamada a _configthreadlocale(_DISABLE_PER_THREAD_LOCALE) hará que ese subproceso use la configuración regional global y cualquier llamada a setlocale en ese subproceso cambiará la configuración regional en todos los subprocesos que no hayan habilitado explícitamente la configuración regional por subproceso.

Para cambiar la configuración regional mediante la biblioteca en tiempo de ejecución de C++, use la clase locale. Al llamar al método locale::global, se cambia la configuración regional en todos los subprocesos que no hayan habilitado explícitamente la configuración regional por subproceso. Para cambiar la configuración regional en un único subproceso o parte de una aplicación, basta con crear una instancia de un objeto locale en ese subproceso o parte del código.

Nota:

La llamada a locale::global cambia la configuración regional de la biblioteca estándar de C++ y la biblioteca en tiempo de ejecución de C. Pero la llamada a setlocale únicamente cambia la configuración regional de la biblioteca en tiempo de ejecución de C; la biblioteca estándar de C++ no se ve afectada.

En los ejemplos siguientes se muestra cómo usar la función setlocale, la clase locale y la función _configthreadlocale para cambiar la configuración regional de una aplicación en varios escenarios diferentes.

Ejemplo: Cambio de la configuración regional con la configuración regional por subproceso habilitada

En este ejemplo, el subproceso principal genera dos subprocesos secundarios. El primero, el subproceso A, habilita la configuración regional por subproceso llamando a _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). El segundo, el subproceso B, así como el subproceso principal, no habilitan la configuración regional por subproceso. Después, el subproceso A cambia la configuración regional con la función setlocale de la biblioteca en tiempo de ejecución de C.

Puesto que el subproceso A tiene habilitada la configuración regional por subproceso, solo las funciones de la biblioteca en tiempo de ejecución de C en el subproceso A empiezan a usar la configuración regional "francés". Las funciones de la biblioteca en tiempo de ejecución de C en el subproceso B y en el principal siguen usando la configuración regional "C". Además, como setlocale no afecta a la configuración regional de la biblioteca estándar de C++, todos los objetos de la biblioteca estándar de C++ siguen usando la configuración regional "C".

// multithread_locale_1.cpp
// compile with: /EHsc /MD
#include <clocale>
#include <cstdio>
#include <locale>
#include <process.h>
#include <windows.h>

#define NUM_THREADS 2
using namespace std;

unsigned __stdcall RunThreadA(void *params);
unsigned __stdcall RunThreadB(void *params);

BOOL localeSet = FALSE;
HANDLE printMutex = CreateMutex(NULL, FALSE, NULL);

int main()
{
    HANDLE threads[NUM_THREADS];

    unsigned aID;
    threads[0] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadA, NULL, 0, &aID);

    unsigned bID;
    threads[1] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadB, NULL, 0, &bID);

    WaitForMultipleObjects(2, threads, TRUE, INFINITE);

    printf_s("[Thread main] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread main] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread main] locale::global is set to \"%s\"\n",
        locale().name().c_str());

    CloseHandle(threads[0]);
    CloseHandle(threads[1]);
    CloseHandle(printMutex);

    return 0;
}

unsigned __stdcall RunThreadA(void *params)
{
    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    setlocale(LC_ALL, "french");
    localeSet = TRUE;

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread A] Per-thread locale is enabled.\n");
    printf_s("[Thread A] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread A] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}

unsigned __stdcall RunThreadB(void *params)
{
    while (!localeSet)
        Sleep(100);

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread B] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread B] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread B] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}
[Thread A] Per-thread locale is enabled.
[Thread A] CRT locale is set to "French_France.1252"
[Thread A] locale::global is set to "C"

[Thread B] Per-thread locale is NOT enabled.
[Thread B] CRT locale is set to "C"
[Thread B] locale::global is set to "C"

[Thread main] Per-thread locale is NOT enabled.
[Thread main] CRT locale is set to "C"
[Thread main] locale::global is set to "C"

Ejemplo: Cambio de la configuración regional global con la configuración regional por subproceso habilitada

En este ejemplo, el subproceso principal genera dos subprocesos secundarios. El primero, el subproceso A, habilita la configuración regional por subproceso llamando a _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). El segundo, el subproceso B, así como el subproceso principal, no habilitan la configuración regional por subproceso. Después, el subproceso A continúa para cambiar la configuración regional con el método locale::global de la biblioteca estándar de C++.

Puesto que el subproceso A tiene habilitada la configuración regional por subproceso, solo las funciones de la biblioteca en tiempo de ejecución de C en el subproceso A empiezan a usar la configuración regional "francés". Las funciones de la biblioteca en tiempo de ejecución de C en el subproceso B y en el principal siguen usando la configuración regional "C". Pero como el método locale::global cambia la configuración regional "de forma global", todos los objetos de la biblioteca estándar de C++ de todos los subprocesos empiezan a usar la configuración regional "francés".

// multithread_locale_2.cpp
// compile with: /EHsc /MD
#include <clocale>
#include <cstdio>
#include <locale>
#include <process.h>
#include <windows.h>

#define NUM_THREADS 2
using namespace std;

unsigned __stdcall RunThreadA(void *params);
unsigned __stdcall RunThreadB(void *params);

BOOL localeSet = FALSE;
HANDLE printMutex = CreateMutex(NULL, FALSE, NULL);

int main()
{
    HANDLE threads[NUM_THREADS];

    unsigned aID;
    threads[0] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadA, NULL, 0, &aID);

    unsigned bID;
    threads[1] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadB, NULL, 0, &bID);

    WaitForMultipleObjects(2, threads, TRUE, INFINITE);

    printf_s("[Thread main] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread main] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread main] locale::global is set to \"%s\"\n",
        locale().name().c_str());

    CloseHandle(threads[0]);
    CloseHandle(threads[1]);
    CloseHandle(printMutex);

    return 0;
}

unsigned __stdcall RunThreadA(void *params)
{
    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    locale::global(locale("french"));
    localeSet = TRUE;

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread A] Per-thread locale is enabled.\n");
    printf_s("[Thread A] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread A] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}

unsigned __stdcall RunThreadB(void *params)
{
    while (!localeSet)
        Sleep(100);

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread B] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread B] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread B] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}
[Thread A] Per-thread locale is enabled.
[Thread A] CRT locale is set to "French_France.1252"
[Thread A] locale::global is set to "French_France.1252"

[Thread B] Per-thread locale is NOT enabled.
[Thread B] CRT locale is set to "C"
[Thread B] locale::global is set to "French_France.1252"

[Thread main] Per-thread locale is NOT enabled.
[Thread main] CRT locale is set to "C"
[Thread main] locale::global is set to "French_France.1252"

Ejemplo: Cambio de la configuración regional sin la configuración regional por subproceso habilitada

En este ejemplo, el subproceso principal genera dos subprocesos secundarios. El primero, el subproceso A, habilita la configuración regional por subproceso llamando a _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). El segundo, el subproceso B, así como el subproceso principal, no habilitan la configuración regional por subproceso. Después, el subproceso B cambia la configuración regional con la función setlocale de la biblioteca en tiempo de ejecución de C.

Puesto que el subproceso B no tiene habilitada la configuración regional por subproceso, las funciones de la biblioteca en tiempo de ejecución de C en el subproceso B y en el subproceso principal empiezan a usar la configuración regional "francés". Las funciones de la biblioteca en tiempo de ejecución de C en el subproceso A siguen usando la configuración regional "C" porque el subproceso A tiene habilitada la configuración regional por subproceso. Además, como setlocale no afecta a la configuración regional de la biblioteca estándar de C++, todos los objetos de la biblioteca estándar de C++ siguen usando la configuración regional "C".

// multithread_locale_3.cpp
// compile with: /EHsc /MD
#include <clocale>
#include <cstdio>
#include <locale>
#include <process.h>
#include <windows.h>

#define NUM_THREADS 2
using namespace std;

unsigned __stdcall RunThreadA(void *params);
unsigned __stdcall RunThreadB(void *params);

BOOL localeSet = FALSE;
BOOL configThreadLocaleCalled = FALSE;
HANDLE printMutex = CreateMutex(NULL, FALSE, NULL);

int main()
{
    HANDLE threads[NUM_THREADS];

    unsigned aID;
    threads[0] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadA, NULL, 0, &aID);

    unsigned bID;
    threads[1] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadB, NULL, 0, &bID);

    WaitForMultipleObjects(2, threads, TRUE, INFINITE);

    printf_s("[Thread main] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread main] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread main] locale::global is set to \"%s\"\n",
        locale().name().c_str());

    CloseHandle(threads[0]);
    CloseHandle(threads[1]);
    CloseHandle(printMutex);

    return 0;
}

unsigned __stdcall RunThreadA(void *params)
{
    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    configThreadLocaleCalled = TRUE;
    while (!localeSet)
        Sleep(100);

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread A] Per-thread locale is enabled.\n");
    printf_s("[Thread A] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread A] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}

unsigned __stdcall RunThreadB(void *params)
{
    while (!configThreadLocaleCalled)
        Sleep(100);
    setlocale(LC_ALL, "french");
    localeSet = TRUE;

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread B] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread B] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread B] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}
[Thread B] Per-thread locale is NOT enabled.
[Thread B] CRT locale is set to "French_France.1252"
[Thread B] locale::global is set to "C"

[Thread A] Per-thread locale is enabled.
[Thread A] CRT locale is set to "C"
[Thread A] locale::global is set to "C"

[Thread main] Per-thread locale is NOT enabled.
[Thread main] CRT locale is set to "French_France.1252"
[Thread main] locale::global is set to "C"

Ejemplo: Cambio de la configuración regional global sin la configuración regional por subproceso habilitada

En este ejemplo, el subproceso principal genera dos subprocesos secundarios. El primero, el subproceso A, habilita la configuración regional por subproceso llamando a _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). El segundo, el subproceso B, así como el subproceso principal, no habilitan la configuración regional por subproceso. Después, el subproceso B cambia la configuración regional con el método locale::global de la biblioteca estándar de C++.

Puesto que el subproceso B no tiene habilitada la configuración regional por subproceso, las funciones de la biblioteca en tiempo de ejecución de C en el subproceso B y en el subproceso principal empiezan a usar la configuración regional "francés". Las funciones de la biblioteca en tiempo de ejecución de C en el subproceso A siguen usando la configuración regional "C" porque el subproceso A tiene habilitada la configuración regional por subproceso. Pero como el método locale::global cambia la configuración regional "de forma global", todos los objetos de la biblioteca estándar de C++ de todos los subprocesos empiezan a usar la configuración regional "francés".

// multithread_locale_4.cpp
// compile with: /EHsc /MD
#include <clocale>
#include <cstdio>
#include <locale>
#include <process.h>
#include <windows.h>

#define NUM_THREADS 2
using namespace std;

unsigned __stdcall RunThreadA(void *params);
unsigned __stdcall RunThreadB(void *params);

BOOL localeSet = FALSE;
BOOL configThreadLocaleCalled = FALSE;
HANDLE printMutex = CreateMutex(NULL, FALSE, NULL);

int main()
{
    HANDLE threads[NUM_THREADS];

    unsigned aID;
    threads[0] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadA, NULL, 0, &aID);

    unsigned bID;
    threads[1] = (HANDLE)_beginthreadex(
        NULL, 0, RunThreadB, NULL, 0, &bID);

    WaitForMultipleObjects(2, threads, TRUE, INFINITE);

    printf_s("[Thread main] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread main] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread main] locale::global is set to \"%s\"\n",
        locale().name().c_str());

    CloseHandle(threads[0]);
    CloseHandle(threads[1]);
    CloseHandle(printMutex);

    return 0;
}

unsigned __stdcall RunThreadA(void *params)
{
    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
    configThreadLocaleCalled = TRUE;
    while (!localeSet)
        Sleep(100);

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread A] Per-thread locale is enabled.\n");
    printf_s("[Thread A] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread A] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}

unsigned __stdcall RunThreadB(void *params)
{
    while (!configThreadLocaleCalled)
        Sleep(100);
    locale::global(locale("french"));
    localeSet = TRUE;

    WaitForSingleObject(printMutex, INFINITE);
    printf_s("[Thread B] Per-thread locale is NOT enabled.\n");
    printf_s("[Thread B] CRT locale is set to \"%s\"\n",
        setlocale(LC_ALL, NULL));
    printf_s("[Thread B] locale::global is set to \"%s\"\n\n",
        locale().name().c_str());
    ReleaseMutex(printMutex);

    return 1;
}
[Thread B] Per-thread locale is NOT enabled.
[Thread B] CRT locale is set to "French_France.1252"
[Thread B] locale::global is set to "French_France.1252"

[Thread A] Per-thread locale is enabled.
[Thread A] CRT locale is set to "C"
[Thread A] locale::global is set to "French_France.1252"

[Thread main] Per-thread locale is NOT enabled.
[Thread main] CRT locale is set to "French_France.1252"
[Thread main] locale::global is set to "French_France.1252"

Consulte también

Compatibilidad del código antiguo con multithreading (Visual C++)
_beginthread, _beginthreadex
_configthreadlocale
setlocale
Internacionalización
Configuración regional
<clocale>
<configuración regional>
locale (Clase)