Udostępnij za pośrednictwem


Unikanie rywalizacji stert

Domyślne menedżery ciągów udostępniane przez MFC i ATL są prostymi otokami na stosie globalnym. Ta globalna sterta jest w pełni bezpieczna wątkowo, co oznacza, że wiele wątków może jednocześnie przydzielić i zwolnić pamięć bez uszkodzenia sterty. Aby zapewnić bezpieczeństwo wątków, sterta musi serializować dostęp do samego siebie. Jest to zwykle realizowane za pomocą sekcji krytycznej lub podobnego mechanizmu blokowania. Za każdym razem, gdy dwa wątki próbują uzyskać dostęp do sterta jednocześnie, jeden wątek jest blokowany do momentu zakończenia żądania drugiego wątku. W przypadku wielu aplikacji ta sytuacja występuje rzadko, a wpływ mechanizmu blokowania sterty na wydajność jest niewielki. Jednak w przypadku aplikacji, które często uzyskują dostęp do sterta z wielu wątków rywalizacji o blokadę sterta, może spowodować, że aplikacja działa wolniej niż w przypadku pojedynczego wątku (nawet na maszynach z wieloma procesorami CPU).

Aplikacje korzystające z CStringT są szczególnie podatne na rywalizację sterty, ponieważ operacje na CStringT obiektach często wymagają reallokowania buforu ciągów.

Jednym ze sposobów złagodzenia rywalizacji o stertę między wątkami jest przydzielenie ciągów z prywatnego, wątkowego stosu lokalnego. Dopóki ciągi przydzielone za pomocą alokatora określonego wątku są używane tylko w tym wątku, alokator nie musi być bezpieczny wątkowo.

Przykład

W poniższym przykładzie przedstawiono procedurę wątku, która przydziela własną prywatną stertę niezwiązaną z wątkiem do użycia dla ciągów w tym wątku:

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);
}

Komentarze

Wiele wątków może być uruchomionych przy użyciu tej samej procedury wątku, ale ponieważ każdy wątek ma własną stertę, nie ma rywalizacji między wątkami. Ponadto fakt, że każda sterta nie jest bezpieczna wątkowo, daje mierzalny wzrost wydajności, nawet jeśli tylko jedna kopia wątku jest uruchomiona. Jest to wynik, że sterta nie używa kosztownych operacji połączonych w celu ochrony przed równoczesnym dostępem.

W przypadku bardziej skomplikowanej procedury wątku może być wygodne przechowywanie wskaźnika do menedżera ciągów wątku w miejscu magazynu lokalnego wątku (TLS). Umożliwia to innym funkcjom wywoływanym przez procedurę wątku dostęp do menedżera ciągów wątku.

Zobacz też

Zarządzanie pamięcią za pomocą CStringT