Compartilhar via


Multithread e localidades

A Biblioteca de Runtime de C e a Biblioteca padrão de C++ dão suporte para alterar a localidade do programa. Este tópico aborda os problemas que surgem ao usar a funcionalidade de localidade de ambas as bibliotecas em um aplicativo com multithread.

Comentários

Com a Biblioteca de Runtime de C, você pode criar aplicativos multithread usando as funções _beginthread e _beginthreadex. Este tópico aborda apenas aplicativos multithread criados usando essas funções. Para obter mais informações, consulte _beginthread, _beginthreadex.

Para alterar a localidade usando a Biblioteca de Runtime de C, use a função setlocale. Nas versões anteriores do Visual C++, essa função sempre modificava a localidade em todo o aplicativo. Agora há suporte para definir a localidade por thread. Isso é feito usando a função _configthreadlocale. Para especificar que setlocale só deve alterar a localidade no thread atual, chame _configthreadlocale(_ENABLE_PER_THREAD_LOCALE)nesse thread. Por outro lado, a chamada _configthreadlocale(_DISABLE_PER_THREAD_LOCALE) fará com que esse thread use a localidade global, e qualquer chamada para setlocale nesse thread alterará a localidade em todos os threads que não habilitaram explicitamente a localidade por thread.

Para alterar a localidade usando a Biblioteca de Runtime de C++, use a classe locale. Ao chamar o método locale::global, você altera a localidade em cada thread que não habilitou explicitamente a localidade por thread. Para alterar a localidade em um único thread ou parte de um aplicativo, basta criar uma instância de um objeto locale nesse thread ou parte do código.

Observação

Chamar locale::global altera a localidade para a Biblioteca padrão de C++ e a Biblioteca de Runtime de C. No entanto, chamar setlocale altera apenas a localidade da Biblioteca de Runtime de C; a Biblioteca Padrão de C++ não é afetada.

Os exemplos a seguir mostram como usar a função setlocale, a classe locale e a função _configthreadlocale para alterar a localidade de um aplicativo em vários cenários diferentes.

Exemplo: alterar a localidade com a localidade por thread habilitada

Neste exemplo, o thread principal gera dois threads filho. O primeiro thread, Thread A, habilita a localidade por thread chamando _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). O segundo thread, Thread B, bem como o thread principal, não habilita a localidade por thread. Em seguida, o Thread A continua a alterar a localidade usando a função setlocale da Biblioteca de Runtime de C.

Como o Thread A tem a localidade por thread habilitada, somente as funções da Biblioteca de Runtime de C no Thread A começam a usar a localidade "french". As funções da Biblioteca de Runtime de C no Thread B e no thread principal continuam a usar a localidade "C". Além disso, como setlocale não afeta a localidade da Biblioteca padrão de C++, todos os objetos da Biblioteca padrão de C++ continuam a usar a localidade "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"

Exemplo: alterar a localidade global com a localidade por thread habilitada

Neste exemplo, o thread principal gera dois threads filho. O primeiro thread, Thread A, habilita a localidade por thread chamando _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). O segundo thread, Thread B, bem como o thread principal, não habilita a localidade por thread. Em seguida, o Thread A continua a alterar a localidade usando o método locale::global da Biblioteca padrão de C++.

Como o Thread A tem a localidade por thread habilitada, somente as funções da Biblioteca de Runtime de C no Thread A começam a usar a localidade "french". As funções da Biblioteca de Runtime de C no Thread B e no thread principal continuam a usar a localidade "C". No entanto, como o método locale::global altera a localidade "globalmente", todos os objetos da Biblioteca padrão de C++ em todos os threads começam a usar a localidade "french".

// 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"

Exemplo: alterar a localidade sem a localidade por thread habilitada

Neste exemplo, o thread principal gera dois threads filho. O primeiro thread, Thread A, habilita a localidade por thread chamando _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). O segundo thread, Thread B, bem como o thread principal, não habilita a localidade por thread. Em seguida, o Thread B continua a alterar a localidade usando a função setlocale da Biblioteca de Runtime de C.

Como o Thread B não tem a localidade por thread habilitada, as funções da Biblioteca de Runtime de C no Thread B e no thread principal começam a usar a localidade "french". As funções da Biblioteca de Runtime de C no Thread A continuam a usar a localidade "C" porque o Thread A tem a localidade por thread habilitada. Além disso, como setlocale não afeta a localidade da Biblioteca padrão de C++, todos os objetos da Biblioteca padrão de C++ continuam a usar a localidade "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"

Exemplo: alterar a localidade global sem a localidade por thread habilitada

Neste exemplo, o thread principal gera dois threads filho. O primeiro thread, Thread A, habilita a localidade por thread chamando _configthreadlocale(_ENABLE_PER_THREAD_LOCALE). O segundo thread, Thread B, bem como o thread principal, não habilita a localidade por thread. Em seguida, o Thread B continua a alterar a localidade usando o método locale::global da Biblioteca padrão de C++.

Como o Thread B não tem a localidade por thread habilitada, as funções da Biblioteca de Runtime de C no Thread B e no thread principal começam a usar a localidade "french". As funções da Biblioteca de Runtime de C no Thread A continuam a usar a localidade "C" porque o Thread A tem a localidade por thread habilitada. No entanto, como o método locale::global altera a localidade "globalmente", todos os objetos da Biblioteca padrão de C++ em todos os threads começam a usar a localidade "french".

// 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"

Confira também

Suporte de multithreading para código anterior (Visual C++)
_beginthread, _beginthreadex
_configthreadlocale
setlocale
Internacionalização
Localidade
<clocale>
<localidade>
Classe locale