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

И библиотека 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++ времени выполнения, воспользуйтесь функцией Класс locale. Вызов метода locale::global изменяет языковой стандарт во всех потоках, которым явно не присвоен языковой стандарт для одного потока. Чтобы изменить языковой стандарт в одном потоке или части приложения, просто создайте в этом потоке или части кода экземпляр объекта locale.

Примечание

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

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

Пример

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

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

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

Поскольку в потоке A включен языковой стандарт для отдельного потока, "французский" языковой стандарт будут использовать только функции библиотеки C времени выполнения в потоке A. Функции библиотеки C времени выполнения в потоке B и в главном потоке по-прежнему будут использовать стандарт "С". Однако в связи с тем, что метод locale::global изменяет языковой стандарт в глобальном режиме, все объекты стандартной библиотеки 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;
}
  

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

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

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

Поскольку в потоке B не включен языковой стандарт для отдельного потока, функции библиотеки C времени выполнения в потоке B и в главном потоке будут использовать "французский" языковой стандарт. Функции библиотеки C времени выполнения в потоке A по-прежнему будут использовать языковой стандарт "С", так как в потоке A включен языковой стандарт для отдельного потока. Однако в связи с тем, что метод locale::global изменяет языковой стандарт в глобальном режиме, все объекты стандартной библиотеки 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;
}
  

См. также

Ссылки

_beginthread, _beginthreadex

_configthreadlocale

setlocale

Интернационализация

Языковой стандарт

<clocale>

<locale>

Класс locale

Основные понятия

Поддержка многопоточности для устаревшего кода (Visual C++)