Udostępnij za pośrednictwem


Pamięć lokalna wątku: Thread-Relative pola statyczne i miejsca danych

Do przechowywania danych unikatowych dla wątku i domeny aplikacji można użyć zarządzanego magazynu lokalnego wątku (TLS). Platforma .NET udostępnia dwa sposoby korzystania z zarządzanego TLS: względne względem wątku pola statyczne i sloty danych.

  • Użyj pól statycznych względnych dla wątków (pól względnych dla wątków Shared w Visual Basic), jeśli możesz przewidzieć dokładne potrzeby w czasie kompilacji. Statyczne pola powiązane z wątkiem zapewniają najlepszą wydajność. Zapewniają one również korzyści sprawdzania typów w czasie kompilacji.

  • Używaj gniazd danych, gdy rzeczywiste wymagania mogą zostać odkryte tylko w czasie wykonywania programu. Miejsca danych są wolniejsze i bardziej niewygodne w użyciu niż statyczne pola względne wątków, a dane są przechowywane jako typ Object, więc przed użyciem należy je przekonwertować na poprawny typ.

W niezarządzanym języku C++ można użyć TlsAlloc do dynamicznego przydzielania miejsc i __declspec(thread) do zadeklarowania, że zmienna powinna zostać przydzielona w magazynie względnym wątku. Pola statyczne względne do wątku oraz miejsca przechowywania danych zapewniają zarządzaną wersję tego zachowania.

Możesz użyć System.Threading.ThreadLocal<T> klasy , aby utworzyć obiekty lokalne wątku, które są inicjowane z opóźnieniem, gdy obiekt jest najpierw używany. Aby uzyskać więcej informacji, zobacz Lazy Initialization.

Unikatowość danych w zarządzanym protokole TLS

Niezależnie od tego, czy używasz względnych pól statycznych wątków, czy miejsc danych w zarządzanym protokole TLS, są unikatowe dla kombinacji domeny wątków i aplikacji.

  • W domenie aplikacji jeden wątek nie może modyfikować danych z innego wątku, nawet jeśli oba wątki używają tego samego pola lub miejsca.

  • Gdy wątek uzyskuje dostęp do tego samego pola lub miejsca z wielu domen aplikacji, w każdej domenie aplikacji jest przechowywana oddzielna wartość.

Jeśli na przykład wątek ustawia wartość względnie statycznego pola wątku, wchodzi do innej domeny aplikacji, a następnie pobiera wartość tego pola, wartość pobrana w drugiej domenie aplikacji różni się od wartości w pierwszej domenie aplikacji. Ustawienie nowej wartości pola w drugiej domenie aplikacji nie wpływa na wartość pola w pierwszej domenie aplikacji.

Podobnie, gdy wątek pobiera to samo nazwane miejsce danych w dwóch różnych domenach aplikacji, dane w pierwszej domenie aplikacji pozostają niezależne od danych w drugiej domenie aplikacji.

Thread-Relative pola statyczne

Jeśli wiesz, że element danych jest zawsze unikatowy dla kombinacji wątku i domeny aplikacji, zastosuj ThreadStaticAttribute atrybut do pola statycznego. Używaj tego pola tak, jak używałbyś każdego innego pola statycznego. Dane w polu są unikatowe dla każdego wątku, który go używa.

Pola statyczne powiązane z wątkiem zapewniają lepszą wydajność niż gniazda danych i pozwalają na sprawdzanie typów podczas kompilacji.

Należy pamiętać, że kod konstruktora klasy zostanie uruchomiony na pierwszym wątku w pierwszym kontekście, który uzyska dostęp do pola. We wszystkich innych wątkach lub kontekstach w tej samej domenie aplikacji pola zostaną zainicjowane do null (Nothing w Visual Basic), jeśli są typami referencyjnymi lub wartościami domyślnymi, jeśli są typami wartości. W związku z tym nie należy polegać na konstruktorach klas do inicjowania pól statycznych względem wątku. Zamiast tego unikaj inicjowania wątkowo-relatywnych pól statycznych i zakładaj, że są inicjowane na null (Nothing) lub ich wartości domyślne.

Sloty danych

Platforma .NET udostępnia dynamiczne miejsca danych, które są unikatowe dla kombinacji domeny wątków i aplikacji. Istnieją dwa typy przedziałów danych: przedziały nazwane i przedziały nienazwane. Obie są implementowane przy użyciu LocalDataStoreSlot struktury.

Zarówno dla slotów nazwanych, jak i nienazwanych, użyj metod Thread.SetData i Thread.GetData, aby ustawić i pobrać informacje w slocie. Są to metody statyczne, które zawsze działają na danych dla aktualnie wykonywanego wątku.

Nazwane sloty mogą być wygodne, ponieważ można pobrać slot, gdy jest potrzebny, przekazując jego nazwę do metody GetNamedDataSlot, zamiast utrzymywać odwołanie do slotu bez nazwy. Jeśli jednak inny składnik używa tej samej nazwy dla jego magazynu wątkowo-zależnego, a wątek wykonuje kod zarówno z Twojego składnika, jak i drugiego składnika, te dwa składniki mogą wzajemnie uszkodzić swoje dane. (W tym scenariuszu przyjęto założenie, że oba składniki są uruchomione w tej samej domenie aplikacji i że nie są przeznaczone do udostępniania tych samych danych).

Zobacz także