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