マルチスレッドとロケール

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 メソッドを呼び出すことで、スレッドごとのロケールが明示的に有効になっていない、すべてのスレッド内のロケールを変更します。 1 つのスレッドまたはアプリケーションの一部でロケールを変更するには、そのスレッドまたはコードの一部に、locale オブジェクトのインスタンスを作成するだけです。

Note

locale::global を呼び出すと、C++ 標準ライブラリと C ランタイム ライブラリの両方のロケールが変更されます。 ただし、setlocale を呼び出すことで変更されるのは、C ランタイム ライブラリのロケールのみです。C++ 標準ライブラリは影響を受けません。

次の例は、いくつかの異なるシナリオで、setlocale 関数、locale クラス_configthreadlocale 関数を使用して、アプリケーションのロケールを変更する方法を示しています。

例: スレッドごとのロケールを有効にしてロケールを変更する

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。 最初のスレッド (スレッド A) は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出して、スレッドごとのロケールを有効にします。 2 番目のスレッド (スレッド B) とメイン スレッドでは、スレッドごとのロケールは有効になりません。 その後、スレッド A は、C ランタイム ライブラリの setlocale 関数を使用して、ロケールの変更に進みます。

スレッド A ではスレッドごとのロケールが有効になっているため、スレッド A 内の C ランタイム ライブラリ関数のみが、"フランス語" ロケールを使用して開始されます。 スレッド B とメイン スレッド内の C ランタイム ライブラリ関数では、"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"

例: スレッドごとのロケールを有効にしてグローバル ロケールを変更する

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。 最初のスレッド (スレッド A) は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出して、スレッドごとのロケールを有効にします。 2 番目のスレッド (スレッド B) とメイン スレッドでは、スレッドごとのロケールは有効になりません。 その後、スレッド A は、C++ 標準ライブラリの locale::global メソッドを使用して、ロケールの変更に進みます。

スレッド A ではスレッドごとのロケールが有効になっているため、スレッド A 内の C ランタイム ライブラリ関数のみが、"フランス語" ロケールを使用して開始されます。 スレッド B とメイン スレッド内の C ランタイム ライブラリ関数では、"C" ロケールが引き続き使用されます。 ただし、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;
}
[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"

例: スレッドごとのロケールを有効にせずにロケールを変更する

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。 最初のスレッド (スレッド A) は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出して、スレッドごとのロケールを有効にします。 2 番目のスレッド (スレッド B) とメイン スレッドでは、スレッドごとのロケールは有効になりません。 その後、スレッド B は、C ランタイム ライブラリの setlocale 関数を使用して、ロケールの変更に進みます。

スレッド B ではスレッドごとのロケールが有効になっていないため、スレッド B とメイン スレッド内の C ランタイム ライブラリ関数では、"フランス語" ロケールの使用が開始されます。 スレッド A ではスレッドごとのロケールが有効になっているため、スレッド A 内の C ランタイム ライブラリ関数では、"C" ロケールが引き続き使用されます。 また、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"

例: スレッドごとのロケールを有効にせずにグローバル ロケールを変更する

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。 最初のスレッド (スレッド A) は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出して、スレッドごとのロケールを有効にします。 2 番目のスレッド (スレッド B) とメイン スレッドでは、スレッドごとのロケールは有効になりません。 その後、スレッド B は、C++ 標準ライブラリの locale:: global メソッドを使用して、ロケールの変更に進みます。

スレッド B ではスレッドごとのロケールが有効になっていないため、スレッド B とメイン スレッド内の C ランタイム ライブラリ関数では、"フランス語" ロケールの使用が開始されます。 スレッド A ではスレッドごとのロケールが有効になっているため、スレッド A 内の C ランタイム ライブラリ関数では、"C" ロケールが引き続き使用されます。 ただし、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;
}
[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 クラス