Избежание состязания за кучу
Диспетчеры строк по умолчанию, предоставляемые MFC и ATL, являются простыми оболочками поверх глобальной кучи. Эта глобальная куча полностью безопасна для потоков, что означает, что несколько потоков могут выделять и освобождать память от него одновременно без повреждения кучы. Чтобы обеспечить безопасность потоков, куча должна сериализовать доступ к себе. Обычно это достигается с помощью критического раздела или аналогичного механизма блокировки. Каждый раз, когда два потока пытаются получить доступ к куче одновременно, один поток блокируется до завершения запроса другого потока. Во многих приложениях такая ситуация редко возникает, а влияние механизма блокировки кучи незначительно. Однако для приложений, которые часто обращаются к куче из нескольких потоков для блокировки кучи, могут привести к тому, что приложение будет работать медленнее, чем если бы оно было однопоточным (даже на компьютерах с несколькими ЦП).
Приложения, использующие CStringT, особенно подвержены спору в куче, так как операции с CStringT
объектами часто требуют перемещений буфера строки.
Один из способов облегчения конфликтов куч между потоками заключается в том, чтобы каждый поток выделяет строки из частной локальной кучы. Если строки, выделенные с определенным потоком, используются только в этом потоке, распределитель не должен быть потокобезопасными.
Пример
В приведенном ниже примере показана процедура потока, которая выделяет собственную частную непотокобезопасную кучу для использования строк в этом потоке:
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);
}
Комментарии
Несколько потоков могут выполняться с помощью одной и той же процедуры потока, но так как каждый поток имеет собственную кучу, между потоками не возникает никаких различий. Кроме того, тот факт, что каждая куча не является потокобезопасной, дает измеримое увеличение производительности, даже если выполняется только одна копия потока. Это результат кучи, не использующий дорогостоящие операции с блокировкой для защиты от параллельного доступа.
Для более сложной процедуры потока может быть удобно хранить указатель на диспетчер строк потока в слоте локального хранилища потока (TLS). Это позволяет другим функциям, вызываемым процедурой потока, получить доступ к диспетчеру строк потока.