Многопоточность и языковые стандарты

Библиотека среды выполнения C и стандартная библиотека C++ обеспечивают поддержку изменения языкового стандарта программы. В этом разделе рассматриваются проблемы, возникающие при использовании функций языкового стандарта обеих библиотек в многопоточных приложениях.

Замечания

С помощью библиотеки среды выполнения C можно создавать многопоточные приложения с помощью _beginthread функций и _beginthreadex функций. В этом разделе рассматриваются только многопоточные приложения, созданные с помощью этих функций. Дополнительные сведения см. в _beginthread _beginthreadex.

Чтобы изменить языковой стандарт с помощью библиотеки среды выполнения C, используйте функцию setlocale . В предыдущих версиях Visual C++эта функция всегда изменяет языковой стандарт во всем приложении. Теперь поддерживается настройка языкового стандарта на основе каждого потока. Это делается с помощью функции _configthreadlocale . Чтобы указать, что setlocale должен изменять языковой стандарт только в текущем потоке, вызовите _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) этот поток. И наоборот, вызов _configthreadlocale(_DISABLE_PER_THREAD_LOCALE) приведет к тому, что поток будет использовать глобальный языковой стандарт, и любой вызов setlocale в этом потоке изменит языковой стандарт во всех потоках, которые явно не включили языковой стандарт для каждого потока.

Чтобы изменить языковой стандарт с помощью библиотеки среды выполнения C++, используйте класс языкового стандарта. Вызывая языковой стандарт::global, вы изменяете языковой стандарт в каждом потоке, который явно не включил языковой стандарт для каждого потока. Чтобы изменить языковой стандарт в одном потоке или части приложения, просто создайте экземпляр locale объекта в этом потоке или части кода.

Примечание.

Вызов языкового стандарта::global изменяет языковой стандарт для стандартной библиотеки C++ и библиотеки среды выполнения C. Однако вызов setlocale изменяет языковой стандарт для библиотеки среды выполнения C; стандартная библиотека C++ не затрагивается.

В следующих примерах показано, как использовать функцию setlocale , класс языкового стандарта и функцию _configthreadlocale для изменения языкового стандарта приложения в нескольких различных сценариях.

Пример. Изменение языкового стандарта с включенным языковым стандартом для каждого потока

В этом примере основной поток создает два дочерних потока. Первый поток, Thread A, включает языковой стандарт для каждого потока путем вызова _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). Второй поток, Thread B, а также основной поток, не включает языковой стандарт для каждого потока. Затем поток A переходит к изменению языкового стандарта с помощью функции setlocale библиотеки среды выполнения C.

Так как thread A включает языковой стандарт для каждого потока, только функции библиотеки среды выполнения C в Thread A начинают использовать языковой стандарт "французский". Функции библиотеки среды выполнения C в потоке B и в основном потоке продолжают использовать языковой стандарт "C". Кроме того, так как setlocale не влияет на языковой стандарт стандартной библиотеки C++, все объекты стандартной библиотеки C++ продолжают использовать языковой стандарт 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"

Пример. Изменение глобального языкового стандарта с включенным языковым стандартом для каждого потока

В этом примере основной поток создает два дочерних потока. Первый поток, Thread A, включает языковой стандарт для каждого потока путем вызова _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). Второй поток, Thread B, а также основной поток, не включает языковой стандарт для каждого потока. Затем поток A переходит к изменению языкового стандарта с помощью языкового стандарта::global метода стандартной библиотеки C++.

Так как thread A включает языковой стандарт для каждого потока, только функции библиотеки среды выполнения C в Thread A начинают использовать языковой стандарт "французский". Функции библиотеки среды выполнения C в потоке B и в основном потоке продолжают использовать языковой стандарт "C". Однако, так как глобальный метод языкового стандарта изменяет языковой стандарт "глобально", все объекты стандартной библиотеки C++ во всех потоках начинают использовать "французский" языковой стандарт.

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

Пример. Изменение языкового стандарта без включения языкового стандарта для каждого потока

В этом примере основной поток создает два дочерних потока. Первый поток, Thread A, включает языковой стандарт для каждого потока путем вызова _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). Второй поток, Thread B, а также основной поток, не включает языковой стандарт для каждого потока. Затем поток B переходит к изменению языкового стандарта с помощью функции setlocale библиотеки среды выполнения C.

Так как в потоке B нет языкового стандарта для каждого потока, функции библиотеки среды выполнения C в Thread B и в основном потоке начинаются с языкового стандарта "французский". Функции библиотеки среды выполнения C в Thread A продолжают использовать языковой стандарт C, так как поток A имеет языковой стандарт для каждого потока. Кроме того, так как setlocale не влияет на языковой стандарт стандартной библиотеки C++, все объекты стандартной библиотеки C++ продолжают использовать языковой стандарт 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"

Пример. Изменение глобального языкового стандарта без включения языкового стандарта для каждого потока

В этом примере основной поток создает два дочерних потока. Первый поток, Thread A, включает языковой стандарт для каждого потока путем вызова _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). Второй поток, Thread B, а также основной поток, не включает языковой стандарт для каждого потока. Затем поток B переходит к изменению языкового стандарта с помощью языкового стандарта::global метода стандартной библиотеки C++.

Так как в потоке B нет языкового стандарта для каждого потока, функции библиотеки среды выполнения C в Thread B и в основном потоке начинаются с языкового стандарта "французский". Функции библиотеки среды выполнения C в Thread A продолжают использовать языковой стандарт C, так как поток A имеет языковой стандарт для каждого потока. Однако, так как глобальный метод языкового стандарта изменяет языковой стандарт "глобально", все объекты стандартной библиотеки C++ во всех потоках начинают использовать "французский" языковой стандарт.

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

См. также

Поддержка многопоточности для устаревшего кода (Visual C++)
_beginthread, _beginthreadex
_configthreadlocale
setlocale
Интернационализация
Локаль
<clocale>
<локаль>
Класс locale