使用 Task.Run 时避免捕获当前执行上下文

发布于 2025-01-12 10:47:12 字数 1044 浏览 5 评论 0原文

我想创建/运行任务而不捕获当前执行上下文。这可能吗?

考虑以下示例:

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/未初始化)?

我还尝试了更明确的 Task.Factory 方法的所有重载,但我找不到解决方案。从源代码中还可以看出,Task.Run(Action) 转换为对 Task.InternalStartNew(null, action, null, ..) 的调用,该调用显式传递 null 作为 state 参数,所以我没有看到更明确的方法。

提前致谢

I would like to create/run a task without capturing the current execution context. Is this possible?

Consider the following sample:

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"
}

Is it possible to make the first Console.WriteLine(AsyncLocal.Value); print "" instead of "1" (because AsyncLocal.Value is null/not initialized)?

I also played around with all overloads of the more explicit Task.Factory-method, but I couldn't find a solution. Form the source it can also be seen that Task.Run(Action) translates into a call to Task.InternalStartNew(null, action, null, ..) which explicitly passes null as state-argument, so I dont see a way I could be more explicit.

Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岛徒 2025-01-19 10:47:12

感谢 @canton7 的评论,答案相当简单:您可以使用 ExecutionContext.SuppressFlow

更正了上面的示例:

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"
}

Thanks to @canton7's comment the answer is rather simple: You can prevent the the ExecutionContext from flowing by using ExecutionContext.SuppressFlow.

Corrected above sample:

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"
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文