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>
<ustawienia regionalne>
locale, klasa