2015-07-29 15 views
31

.NET 4.6 wprowadza klasę AsyncLocal<T> dla przepływu danych otoczenia wzdłuż asynchronicznego przepływu sterowania. Wcześniej używałem do tego celu CallContext.LogicalGet/SetData i zastanawiam się, czy iw jaki sposób te dwie są semantycznie odmienne (poza oczywistymi różnicami API, takimi jak silne pisanie i brak zaufania do kluczy strunowych).W jaki sposób semantyka AsyncLocal różni się od kontekstu połączenia logicznego?

+4

Nie sądzę, że istnieje. Jest to alternatywa dla projektów, które nie mogą polegać na CallContext, ponieważ są ukierunkowane na CoreCLR. CallContext wymaga wsparcia zdalnego, niedostępnego w małej wersji CLR. –

+2

Gdzie jest @ stephen-cleary kiedy go potrzebujesz? – batwad

Odpowiedz

26

Semantyka to prawie to samo. Oba są przechowywane w ExecutionContext i przepływają przez połączenia asynchroniczne.

Różnice dotyczą zmian API (zgodnie z opisem) wraz z możliwością zarejestrowania wywołania zwrotnego dla zmian wartości.

Technicznie rzecz biorąc, istnieje duża różnica w realizacji co CallContext klonuje się za każdym razem jest on skopiowany (za pomocą CallContext.Clone) podczas przesyłania danych przez AsyncLocal „s jest trzymane w słowniku ExecutionContext._localValues i właśnie to odniesienie jest kopiowane bez jakiejkolwiek dodatkowej pracy .

Aby upewnić się, że aktualizacje wpływają na bieżący przepływ po zmianie wartości AsyncLocal, tworzony jest nowy słownik, a wszystkie istniejące wartości są płytko kopiowane na nowy.

Ta różnica może być zarówno dobra, jak i zła pod względem wydajności, w zależności od tego, gdzie użyto AsyncLocal.

Teraz, jak Hans Passant wspomniano w komentarzach CallContext został pierwotnie stworzony do usług zdalnych, a nie jest dostępny gdzie remoting nie jest obsługiwane (np .Net rdzenia), które jest prawdopodobnie dlaczego AsyncLocal dodano do ram:

#if FEATURE_REMOTING 
    public LogicalCallContext.Reader LogicalCallContext 
    { 
     [SecurityCritical] 
     get { return new LogicalCallContext.Reader(IsNull ? null : m_ec.LogicalCallContext); } 
    } 

    public IllogicalCallContext.Reader IllogicalCallContext 
    { 
     [SecurityCritical] 
     get { return new IllogicalCallContext.Reader(IsNull ? null : m_ec.IllogicalCallContext); } 
    } 
#endif 

Uwaga: istnieje również AsyncLocal w Visual Studio SDK, który jest zasadniczo otoki nad CallContext który pokazuje, jak bardzo podobne koncepcje są: System.Threading.AsyncLocal.

15

Zastanawiam się, czy iw jaki sposób te dwie rzeczy są semantycznie różni

Z tego co widać, zarówno CallContext i AsyncLocal wewnętrznie przekaźnik na ExecutionContext do przechowywania swoich danych wewnętrznych wewnątrz Dictionary. Ta ostatnia wydaje się dodawać kolejny poziom pośrednictwa dla połączeń asynchronicznych. CallContext istnieje od czasu .NET Remoting i był wygodnym sposobem przesyłania danych między asynchronicznymi połączeniami, gdzie nie było prawdziwej alternatywy, aż do teraz.

Największą różnicę można dostrzec, że AsyncLocal teraz pozwala zarejestrować powiadomień poprzez oddzwonienie gdy bazowego wartość zapisana zostanie zmieniona, albo za pomocą przełącznika ExecutionContext lub jawnie zastępując istniejącą wartość.

// AsyncLocal<T> also provides optional notifications 
// when the value associated with the current thread 
// changes, either because it was explicitly changed 
// by setting the Value property, or implicitly changed 
// when the thread encountered an "await" or other context transition. 
// For example, we might want our 
// current culture to be communicated to the OS as well: 

static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
args => 
{ 
    NativeMethods.SetThreadCulture(args.CurrentValue.LCID); 
}); 

Poza tym, jeden mieszka w System.Threading natomiast drugi mieszka na System.Runtime.Remoting, gdzie były wspierane będą w CoreCLR.

Ponadto nie wydaje się, aby AsyncLocal miał płytką semantykę zapisu na zapis SetLogicalData, więc dane przepływają między połączeniami bez kopiowania.

0

Wydaje się, że istnieje pewna semantyczna różnica w taktowaniu.

Z CallContext zmiana kontekstu zachodzi, gdy kontekst dla wątku potomnego/zadania/metody asynchronicznej jest skonfigurowany, tj. Gdy wywoływane są Task.Factory.StartNew(), Task.Run() lub metoda asynchroniczna.

Przy pomocy AsyncLocal następuje zmiana kontekstu (wywołanie wywołania zwrotnego powiadomień o zmianie), gdy metoda wątku/zadania/asynchronizacji faktycznie zaczyna działać.

Różnica w taktowaniu może być interesująca, szczególnie jeśli chcesz, aby obiekt kontekstu był klonowany przy zmianie kontekstu. Korzystanie z różnych mechanizmów może powodować klonowanie różnych treści: za pomocą CallContext klonujesz zawartość, gdy tworzony jest wątek potomny/zadanie lub wywoływana jest metoda asynchroniczna; ale z AsyncLocal klonujesz zawartość, gdy zaczyna się wykonywanie wątku potomnego/zadania/asynchronizacji, treść obiektu kontekstu mogła zostać zmieniona przez wątek nadrzędny.

+0

Interesujące. Czy istnieje krótki fragment kodu, który możesz opublikować, który pokazuje tę różnicę? – ChaseMedallion

+0

Historycznie (przed .Net 4.6) musieliśmy zhackować specjalny slot w CallContext, aby kontekstowa struktura danych została sklonowana po przełączeniu kontekstu połączenia. Program demonstracyjny pokazuje, że w przypadku CallContext klonowanie odbywa się w wątku wywołującym, w porównaniu z AsyncLocal procedura obsługi powiadomień o zmianach jest wywoływana w wywołanym wątku. https://1drv.ms/u/s!AuD-2O_ZRWVijzXBVJTKbQeWCTzc – WenningQiu

Powiązane problemy