使用 Task.Run 时避免捕获当前执行上下文
Avoid capturing current execution context when using Task.Run
我想 create/run 一个任务而不捕获当前的执行上下文。这可能吗?
考虑以下示例:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void TaskRunCapturesContext()
{
AsyncLocal.Value = 1;
var task = Task.Run(static async () =>
{
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "" <-- Problem HERE
AsyncLocal.Value = 2;
Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
});
Task.WaitAll(task);
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}
是否可以让第一个Console.WriteLine(AsyncLocal.Value);
打印""
而不是"1"
(因为AsyncLocal.Value是null/not初始化的)?
我还尝试了更明确的 Task.Factory 方法的所有重载,但找不到解决方案。从源代码中还可以看出 Task.Run(Action) 转换为对 Task.InternalStartNew(null, action, null, ..)
的调用,它显式地将 null
作为 state
参数传递,所以我看不到任何方法我可以更明确。
提前致谢
感谢@canton7 的评论,答案相当简单:您可以通过使用 ExecutionContext.SuppressFlow.
来防止 ExecutionContext 流动
更正以上示例:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void TaskRunNoLongerCapturesContext()
{
AsyncLocal.Value = 1;
using (ExecutionContext.SuppressFlow()) // <-- THIS
{
var task = Task.Run(static async () =>
{
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(AsyncLocal.Value); // Prints "", expected is "" <-- Hurrah
AsyncLocal.Value = 2;
Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
});
Task.WaitAll(task);
}
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}
我想 create/run 一个任务而不捕获当前的执行上下文。这可能吗?
考虑以下示例:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void TaskRunCapturesContext()
{
AsyncLocal.Value = 1;
var task = Task.Run(static async () =>
{
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "" <-- Problem HERE
AsyncLocal.Value = 2;
Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
});
Task.WaitAll(task);
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}
是否可以让第一个Console.WriteLine(AsyncLocal.Value);
打印""
而不是"1"
(因为AsyncLocal.Value是null/not初始化的)?
我还尝试了更明确的 Task.Factory 方法的所有重载,但找不到解决方案。从源代码中还可以看出 Task.Run(Action) 转换为对 Task.InternalStartNew(null, action, null, ..)
的调用,它显式地将 null
作为 state
参数传递,所以我看不到任何方法我可以更明确。
提前致谢
感谢@canton7 的评论,答案相当简单:您可以通过使用 ExecutionContext.SuppressFlow.
来防止 ExecutionContext 流动更正以上示例:
private static readonly AsyncLocal<object> AsyncLocal = new AsyncLocal<object>();
[TestMethod]
public void TaskRunNoLongerCapturesContext()
{
AsyncLocal.Value = 1;
using (ExecutionContext.SuppressFlow()) // <-- THIS
{
var task = Task.Run(static async () =>
{
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(AsyncLocal.Value); // Prints "", expected is "" <-- Hurrah
AsyncLocal.Value = 2;
Console.WriteLine(AsyncLocal.Value); // Prints "2", expected is "2"
});
Task.WaitAll(task);
}
Console.WriteLine(AsyncLocal.Value); // Prints "1", expected is "1"
}