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

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 Class を使用します。locale::global メソッドを呼び出すことによって、明示的に有効化されたスレッド個別のロケールを持たないすべてのスレッドのロケールが変更されます。単一スレッドまたはアプリケーションの一部のロケールを変更するには、単にそのスレッドまたはコードの該当部分に locale オブジェクトのインスタンスを作成します。

[!メモ]

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

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

使用例

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。最初のスレッドであるスレッド A は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出してスレッド個別のロケールを有効化します。次のスレッドであるスレッド 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;
}
  
  
  
  

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。最初のスレッドであるスレッド A は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出してスレッド個別のロケールを有効化します。次のスレッドであるスレッド 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;
}
  
  
  
  

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。最初のスレッドであるスレッド A は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出してスレッド個別のロケールを有効化します。次のスレッドであるスレッド 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;
}
  
  
  
  

この例では、メイン スレッドによって 2 つの子スレッドが生成されます。最初のスレッドであるスレッド A は、_configthreadlocale(_ENABLE_PER_THREAD_LOCALE) を呼び出してスレッド個別のロケールを有効化します。次のスレッドであるスレッド 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;
}
  
  
  
  

参照

関連項目

_beginthread、_beginthreadex

_configthreadlocale

setlocale

国際化

ロケール

<clocale>

<locale>

locale Class

概念

旧形式のコードのためのマルチスレッド サポート (Visual C++)