다중 스레딩 및 로캘

C 런타임 라이브러리와 C++ 표준 라이브러리는 모두 프로그램의 로캘 변경을 지원합니다. 이 항목에서는 다중 스레드 애플리케이션에서 두 라이브러리의 로캘 기능을 사용할 때 발생하는 문제에 대해 설명합니다.

설명

C 런타임 라이브러리를 사용하면 및 _beginthreadex 함수를 사용하여 다중 스레드 애플리케이션을 _beginthread 만들 수 있습니다. 이 항목에서는 이러한 함수를 사용하여 만든 다중 스레드 애플리케이션에 대해서만 설명합니다. 자세한 내용은 _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 함수를 사용하여 여러 시나리오에서 애플리케이션의 로캘을 변경하는 방법을 보여 줍니다.

예: 스레드별 로캘을 사용하도록 설정된 로캘 변경

이 예제에서 기본 스레드는 두 개의 자식 스레드를 생성합니다. 첫 번째 스레드인 스레드 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;
}
[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"

예: 스레드별 로캘을 사용하도록 설정된 전역 로캘 변경

이 예제에서 기본 스레드는 두 개의 자식 스레드를 생성합니다. 첫 번째 스레드인 스레드 A는 스레드당 로캘을 호출 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)하여 사용하도록 설정합니다. 두 번째 스레드인 스레드 B와 기본 스레드는 스레드별 로캘을 사용하도록 설정하지 않습니다. 그런 다음 스레드 A는 C++ 표준 라이브러리의 locale::global 메서드를 사용하여 로캘을 변경합니다.

스레드 A는 스레드별 로캘을 사용하도록 설정했기 때문에 스레드 A의 C 런타임 라이브러리 함수만 "프랑스어" 로캘을 사용하기 시작합니다. 스레드 B 및 기본 스레드의 C 런타임 라이브러리 함수는 "C" 로캘을 계속 사용합니다. 그러나 로캘::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"

예: 스레드별 로캘을 사용하지 않고 로캘 변경

이 예제에서 기본 스레드는 두 개의 자식 스레드를 생성합니다. 첫 번째 스레드인 스레드 A는 스레드당 로캘을 호출 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)하여 사용하도록 설정합니다. 두 번째 스레드인 스레드 B와 기본 스레드는 스레드별 로캘을 사용하도록 설정하지 않습니다. 그런 다음 스레드 B는 C 런타임 라이브러리의 setlocale 함수를 사용하여 로캘을 변경합니다.

스레드 B에는 스레드별 로캘을 사용할 수 없으므로 스레드 B 및 기본 스레드의 C 런타임 라이브러리 함수는 "프랑스어" 로캘을 사용하기 시작합니다. 스레드 A의 C 런타임 라이브러리 함수는 스레드 A가 스레드별 로캘을 사용하도록 설정되어 있으므로 "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"

예: 스레드별 로캘을 사용하지 않고 전역 로캘 변경

이 예제에서 기본 스레드는 두 개의 자식 스레드를 생성합니다. 첫 번째 스레드인 스레드 A는 스레드당 로캘을 호출 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)하여 사용하도록 설정합니다. 두 번째 스레드인 스레드 B와 기본 스레드는 스레드별 로캘을 사용하도록 설정하지 않습니다. 그런 다음 스레드 B는 C++ 표준 라이브러리의 locale::global 메서드를 사용하여 로캘을 변경합니다.

스레드 B에는 스레드별 로캘을 사용할 수 없으므로 스레드 B 및 기본 스레드의 C 런타임 라이브러리 함수는 "프랑스어" 로캘을 사용하기 시작합니다. 스레드 A의 C 런타임 라이브러리 함수는 스레드 A가 스레드별 로캘을 사용하도록 설정되어 있으므로 "C" 로캘을 계속 사용합니다. 그러나 로캘::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
국제화
Locale
<clocale>
<로캘>
locale 클래스