多线程和区域设置

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 继续使用 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”区域设置。 但是,由于 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"

示例:在未启用每线程区域设置的情况下更改区域设置

在此示例中,主线程生成了两个子线程。 第一个线程(线程 A)通过调用 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE) 启用了每线程区域设置。 第二个线程(线程 B)以及主线程未启用每线程区域设置。 然后,线程 B 继续使用 C 运行时库的 setlocale 函数更改区域设置。

由于线程 B 没有启用每线程区域设置,因此线程 B 和主线程中的 C 运行时库函数开始使用“法语”区域设置。 线程 A 中的 C 运行时库函数继续使用“C”区域设置,因为线程 A 已启用每线程区域设置。 此外,由于 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 运行时库函数继续使用“C”区域设置,因为线程 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;
}
[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 类