В зависимости от того, использую ли я код на основе async/await или код на основе TPL, у меня возникают два разных поведения в отношении очистки логического CallContext
.Очистка CallContext в TPL
можно установить и ясно, логично CallContext
точно так, как я ожидать, если я использую следующий асинхр/ожидают Код:
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")
});
}
}
выше выдает следующее:
{Place = Task.Run , Id = 9, Msg = мир}
{Place = Main, Id = 8, Msg =}
Обратите внимание на Msg =
, который указывает, что CallContext
на основной теме был освобожден и пуст.
Но когда я переключаюсь на чистый TPL код/TAP Я не могу достичь того же эффекта ...
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")
});
}
}
Вышеприведенные выходов следующее:
{Место = Task.Run , Id = 10, Msg = мир}
{Place = Main, Id = 9, Msg = мир}
Что я могу сделать, чтобы заставить TPL, чтобы "освободить" логический CallContext
так же, как и код async/await?
Меня не интересует альтернатива CallContext
.
Я надеюсь, что указанный выше код TPL/TAP будет исправлен, чтобы я мог использовать его в проектах, ориентированных на инфраструктуру .NET. Если это невозможно в .net 4.0, мне все же интересно, если это можно сделать в .net 4.5.
в вашей TPL версии, существует риск того, что логический CallContext будет освобожден до Task.Factory.StartNew имел шанс захватить его? Я также должен быть уверен, что все продолжения (если они есть) из Task.Factory.StartNew действительно имеют CallContext, даже если он был «освобожден» основным потоком. –
@BrentArias вы можете протестировать его с помощью Thread.Sleep (я сделал). Task.Factory.StartNew as do Task.Run захватывает (копирует) контекст и сохраняет его в Задаче, поэтому вам не нужно беспокоиться об этом. У вас больше информации об этом здесь: http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx – i3arnon
@BrentArias * ", когда вы используете Task.Run, вызов Run захватывает ExecutionContext из вызывающего потока, сохраняя этот экземпляр ExecutionContext в объекте Task. Когда делегат, предоставленный Task.Run, позже вызывается как часть выполнения этой задачи, это делается через ExecutionContext.Run, используя сохраненный контекст. Это верно для Task.Run, для ThreadPool.QueueUserWorkItem, для Delegate.BeginInvoke для Stream.BeginRead, для DispatcherSynchronizationContext.Post и для любого другого асинхронного API, о котором вы можете думать. "* – i3arnon