Многопоточность и языковые стандарты
Библиотека среды выполнения 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