Wielowątkowość i ustawienia regionalne

Zarówno biblioteka środowiska uruchomieniowego języka C, jak i standardowa biblioteka języka C++ zapewniają obsługę zmiany ustawień regionalnych programu. W tym temacie omówiono problemy występujące podczas korzystania z funkcji ustawień regionalnych obu bibliotek w aplikacji wielowątkowym.

Uwagi

Za pomocą biblioteki środowiska uruchomieniowego języka C można tworzyć wielowątkowane aplikacje przy użyciu _beginthread funkcji i _beginthreadex . W tym temacie omówiono tylko aplikacje wielowątkowane utworzone przy użyciu tych funkcji. Aby uzyskać więcej informacji, zobacz _beginthread, _beginthreadex.

Aby zmienić ustawienia regionalne przy użyciu biblioteki środowiska uruchomieniowego języka C, użyj funkcji setlocale . W poprzednich wersjach programu Visual C++ta funkcja zawsze modyfikowała ustawienia regionalne w całej aplikacji. Istnieje teraz obsługa ustawiania ustawień regionalnych dla poszczególnych wątków. Odbywa się to przy użyciu funkcji _configthreadlocale . Aby określić, że właściwość setlocale powinna zmienić ustawienia regionalne tylko w bieżącym wątku, wywołaj _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) w tym wątku. Z drugiej strony wywołanie _configthreadlocale(_DISABLE_PER_THREAD_LOCALE) spowoduje, że wątek będzie używać globalnych ustawień regionalnych, a każde wywołanie ustawianialokalnego w tym wątku spowoduje zmianę ustawień regionalnych we wszystkich wątkach, które nie zostały jawnie włączone ustawienia regionalne dla każdego wątku.

Aby zmienić ustawienia regionalne przy użyciu biblioteki środowiska uruchomieniowego języka C++, użyj klasy ustawień regionalnych. Wywołując metodę locale::global , należy zmienić ustawienia regionalne w każdym wątku, który nie włączył jawnie ustawień regionalnych dla każdego wątku. Aby zmienić ustawienia regionalne w jednym wątku lub części aplikacji, po prostu utwórz wystąpienie locale obiektu w tym wątku lub części kodu.

Uwaga

Wywoływanie ustawień regionalnych::global zmienia ustawienia regionalne zarówno dla standardowej biblioteki języka C++, jak i biblioteki środowiska uruchomieniowego języka C. Jednak wywołanie setlocale zmienia tylko ustawienia regionalne dla biblioteki środowiska uruchomieniowego języka C; biblioteka Standardowa języka C++ nie ma wpływu.

W poniższych przykładach pokazano, jak używać funkcji setlocale , klasy ustawień regionalnych i funkcji _configthreadlocale w celu zmiany ustawień regionalnych aplikacji w kilku różnych scenariuszach.

Przykład: zmienianie ustawień regionalnych z włączonymi ustawieniami regionalnymi na wątek

W tym przykładzie główny wątek powoduje zduplikowane dwa wątki podrzędne. Pierwszy wątek, Thread A, włącza ustawienia regionalne dla każdego wątku przez wywołanie ._configthreadlocale(_ENABLE_PER_THREAD_LOCALE) Drugi wątek, Thread B, a także główny wątek, nie włączaj ustawień regionalnych dla każdego wątku. Wątek A następnie przechodzi do zmiany ustawień regionalnych przy użyciu funkcji setlocale biblioteki środowiska uruchomieniowego języka C.

Ponieważ wątek A ma włączone ustawienia regionalne dla wątku, tylko funkcje biblioteki środowiska uruchomieniowego języka C w wątku A zaczynają korzystać z ustawień regionalnych "francuskich". Funkcje biblioteki środowiska uruchomieniowego języka C w wątku B i w wątku głównym nadal używają ustawień regionalnych "C". Ponadto, ponieważ setlocale nie ma wpływu na ustawienia regionalne biblioteki standardowej języka C++, wszystkie obiekty biblioteki standardowej języka C++ nadal używają ustawień regionalnych "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"

Przykład: Zmiana globalnych ustawień regionalnych z włączonymi ustawieniami regionalnymi dla każdego wątku

W tym przykładzie główny wątek powoduje zduplikowane dwa wątki podrzędne. Pierwszy wątek, Thread A, włącza ustawienia regionalne dla każdego wątku przez wywołanie ._configthreadlocale(_ENABLE_PER_THREAD_LOCALE) Drugi wątek, Thread B, a także główny wątek, nie włączaj ustawień regionalnych dla każdego wątku. Wątek A następnie przechodzi do zmiany ustawień regionalnych przy użyciu metody locale::global biblioteki standardowej języka C++.

Ponieważ wątek A ma włączone ustawienia regionalne dla wątku, tylko funkcje biblioteki środowiska uruchomieniowego języka C w wątku A zaczynają korzystać z ustawień regionalnych "francuskich". Funkcje biblioteki środowiska uruchomieniowego języka C w wątku B i w wątku głównym nadal używają ustawień regionalnych "C". Jednak ponieważ metoda locale::global zmienia ustawienia regionalne "globalnie", wszystkie obiekty biblioteki standardowej języka C++ we wszystkich wątkach zaczynają używać ustawień regionalnych "francuskich".

// 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"

Przykład: zmienianie ustawień regionalnych bez ustawień regionalnych dla wątku

W tym przykładzie główny wątek powoduje zduplikowane dwa wątki podrzędne. Pierwszy wątek, Thread A, włącza ustawienia regionalne dla każdego wątku przez wywołanie ._configthreadlocale(_ENABLE_PER_THREAD_LOCALE) Drugi wątek, Thread B, a także główny wątek, nie włączaj ustawień regionalnych dla każdego wątku. Wątek B następnie przechodzi do zmiany ustawień regionalnych przy użyciu funkcji setlocale biblioteki środowiska uruchomieniowego języka C.

Ponieważ w wątku B nie włączono ustawień regionalnych dla każdego wątku, biblioteka środowiska uruchomieniowego języka C w wątku B i w głównym wątku zaczynają korzystać z ustawień regionalnych "francuskich". Funkcje biblioteki środowiska uruchomieniowego języka C w wątku A nadal używają ustawień regionalnych "C", ponieważ wątek A ma włączone ustawienia regionalne dla wątku. Ponadto, ponieważ setlocale nie ma wpływu na ustawienia regionalne biblioteki standardowej języka C++, wszystkie obiekty biblioteki standardowej języka C++ nadal używają ustawień regionalnych "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"

Przykład: zmienianie globalnych ustawień regionalnych bez ustawień regionalnych dla każdego wątku

W tym przykładzie główny wątek powoduje zduplikowane dwa wątki podrzędne. Pierwszy wątek, Thread A, włącza ustawienia regionalne dla każdego wątku przez wywołanie ._configthreadlocale(_ENABLE_PER_THREAD_LOCALE) Drugi wątek, Thread B, a także główny wątek, nie włączaj ustawień regionalnych dla każdego wątku. Wątek B następnie przechodzi do zmiany ustawień regionalnych przy użyciu metody locale::global biblioteki standardowej języka C++.

Ponieważ w wątku B nie włączono ustawień regionalnych dla każdego wątku, biblioteka środowiska uruchomieniowego języka C w wątku B i w głównym wątku zaczynają korzystać z ustawień regionalnych "francuskich". Funkcje biblioteki środowiska uruchomieniowego języka C w wątku A nadal używają ustawień regionalnych "C", ponieważ wątek A ma włączone ustawienia regionalne dla wątku. Jednak ponieważ metoda locale::global zmienia ustawienia regionalne "globalnie", wszystkie obiekty biblioteki standardowej języka C++ we wszystkich wątkach zaczynają używać ustawień regionalnych "francuskich".

// 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"

Zobacz też

Obsługa wielowątkowości w przypadku starszego kodu (Visual C++)
_beginthread, _beginthreadex
_configthreadlocale
setlocale
Internacjonalizacja
ustawienia regionalne
<clocale>
<Ustawień regionalnych>
locale, klasa