Evitar contenção de heap
Os gerenciadores de cadeia de caracteres padrão fornecidos por MFC e ATL são wrappers simples sobre um heap global. Esse heap global é totalmente thread-safe, o que significa que vários threads podem alocar e liberar memória dele simultaneamente sem corromper o heap. Para ajudar a fornecer acesso thread-safe, o heap precisa serializar o acesso a si mesmo. Isso geralmente é feito com uma seção crítica ou mecanismo de bloqueio semelhante. Sempre que dois threads tentam acessar o heap simultaneamente, um thread é bloqueado até que a solicitação do outro thread seja concluída. Para muitos aplicativos, essa situação raramente ocorre e o impacto no desempenho do mecanismo de bloqueio do heap é insignificante. No entanto, para aplicativos que acessam frequentemente o heap de vários threads de contenção para o bloqueio do heap pode fazer com que o aplicativo seja executado mais lento do que se ele fosse de thread único (mesmo em computadores com várias CPUs).
Aplicativos que usam CStringT são especialmente suscetíveis à contenção de heap porque as operações em objetos CStringT
frequentemente exigem realocação do buffer de cadeia de caracteres.
Uma maneira de aliviar a contenção de heap entre threads é fazer com que cada thread aloque cadeias de caracteres de um heap local de thread privado. Desde que as cadeias de caracteres alocadas com o alocador de um thread específico sejam usadas somente nesse thread, o alocador não precisa ser thread-safe.
Exemplo
O exemplo a seguir ilustra um procedimento de thread que aloca seu próprio heap privado não thread-safe a ser usado para cadeias de caracteres nesse thread:
DWORD WINAPI WorkerThreadProc(void* pBase)
{
// Declare a non-thread-safe heap just for this thread:
CWin32Heap stringHeap(HEAP_NO_SERIALIZE, 0, 0);
// Declare a string manager that uses the thread's heap:
CAtlStringMgr stringMgr(&stringHeap);
int nBase = *((int*)pBase);
int n = 1;
for(int nPower = 0; nPower < 10; nPower++)
{
// Use the thread's string manager, instead of the default:
CString strPower(&stringMgr);
strPower.Format(_T("%d"), n);
_tprintf_s(_T("%s\n"), strPower);
n *= nBase;
}
return(0);
}
Comentários
Vários threads podem estar em execução usando esse mesmo procedimento de thread, mas como cada thread tem seu próprio heap, não há contenção entre threads. Além disso, o fato de que cada heap não é thread-safe dá um aumento mensurável no desempenho, mesmo que apenas uma cópia do thread esteja em execução. Isso é o resultado do heap não usar operações interligadas caras para proteger contra acesso simultâneo.
Para um procedimento de thread mais complexo, pode ser conveniente armazenar um ponteiro para o gerenciador de cadeias de caracteres do thread em um slot do TLS (armazenamento local de thread). Isso permite que outras funções chamadas pelo procedimento de thread acessem o gerenciador de cadeias de caracteres do thread.