W zależności od tego, czy korzystam z kodu asynchronicznego/opartego na oczekiwaniach, czy kodu opartego na licencji TPL, otrzymuję dwa różne zachowania dotyczące czyszczenia logicznego CallContext
.Czyszczenie CallContext w języku TPL
mogę ustawić i jasne, logiczne CallContext
dokładnie tak, jak ja się spodziewać, jeśli mogę użyć następującego asynchronicznie/czekają Kod:
class Program
{
static async Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
await Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
Powyższy wyprowadza następujące:
{Place = Task.Run , J = 9, MSG = świecie}
{miejsce = Menem, J = 8, MSG =}
Zawiadomienie Msg =
co oznacza, że CallContext
na głównym wątku zostało zwolnione i jest puste.
Ale kiedy przełączyć się na czystej OC/code TAP nie mogę osiągnąć ten sam efekt ...
class Program
{
static Task DoSomething()
{
CallContext.LogicalSetData("hello", "world");
var result = Task.Run(() =>
Debug.WriteLine(new
{
Place = "Task.Run",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
}))
.ContinueWith((t) =>
CallContext.FreeNamedDataSlot("hello")
);
return result;
}
static void Main(string[] args)
{
DoSomething().Wait();
Debug.WriteLine(new
{
Place = "Main",
Id = Thread.CurrentThread.ManagedThreadId,
Msg = CallContext.LogicalGetData("hello")
});
}
}
Powyższe wyjść następuje:
{Place = Task.Run Id = 10, Msg = świat}
{Place = Main, id = 9, Msg = świat}
Czy mogę coś zrobić, aby zmusić do OC "wolne" na logiczny CallContext
w taki sam sposób jak robi to kod async/await? Nie jestem zainteresowany alternatywami do CallContext
.
Mam nadzieję, że uda mi się naprawić powyższy kod TPL/TAP, aby móc go używać w projektach ukierunkowanych na platformę .net 4.0. Jeśli nie jest to możliwe w .net 4.0, nadal jestem ciekawy, czy można to zrobić w .net 4.5.
w wersji OC, to istnieje ryzyko, że logiczne CallContext zostanie zwolniona przed Task.Factory.StartNew miał szansę przechwycić? Muszę także mieć pewność, że wszystkie kontynuacje (jeśli istnieją) od Task.Factory.StartNew rzeczywiście mają CallContext, nawet jeśli został "uwolniony" przez główny wątek. –
@BrentArias można przetestować za pomocą Thread.Sleep (ja zrobiłem). Task.Factory.StartNew tak jak Task.Run przechwytuj (kopiuj) kontekst i przechowuj go w zadaniu, aby nie martwić się o to. Więcej informacji na ten temat można znaleźć tutaj: http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx – i3arnon
@BrentArias * "podczas korzystania z Task.Run, call to Run przechwytuje obiekt ExecutionContext z wątku wywołującego, przechowując tę instancję ExecutionContext w obiekcie Task. Gdy delegat przekazany do Task.Run zostanie później wywołany w ramach wykonywania tego zadania, zostanie to wykonane przez ExecutionContext.Run, używając zapisanego kontekstu. Dotyczy to Task.Run, dla ThreadPool.QueueUserWorkItem, dla Delegate.BeginInvoke, dla Stream.BeginRead, dla DispatcherSynchronizationContext.Post i dla każdego innego asynchronicznego API, jakie możesz wymyślić. "* – i3arnon