如何不在 Task.Factory.StartNew 中传播 Activity

How to not propagate Activity in Task.Factory.StartNew

我有以下代码:

Task task = null;
var channel = System.Threading.Channels.Channel.CreateUnbounded<string>();

using (var activity = MyActivitySource.StartActivity("Parent"))
{
    task = Task.Factory.StartNew(async () =>
    {
        //Activity.Current = null;
        var item = await channel.Reader.ReadAsync();
        Console.WriteLine("Task: {0}", Activity.Current?.DisplayName);
    });
}

Console.WriteLine("Current end: {0}", Activity.Current?.DisplayName ?? "(null)");

await channel.Writer.WriteAsync("something");
await task;

我想在不注入 Activity 的情况下开始任务。 我无法在 using(var acrivity...) 之外创建任务。

一个选项(我想)是在任务开始时设置 Activity.Current = null。 有其他选择吗?

One option (I suppose) is setting Activity.Current = null at the beginning of the task. Is there an alternative option?

我的首选解决方案是将“处理”代码移动到单独的 top-level 循环中。即,将当前在 StartNew 中的代码移动到一个完全独立的方法中,将其包装在 await foreach (var item in channel.Reader.ReadAllAsync()) 中,并在您启动频道时启动该循环。因此,本质上使它成为某种 ActionBlock

您可能需要使用其他值来扩充当前的 string 项目才能使其发挥作用。即,如果您需要 per-item 完成,那么您可以将 string 替换为 (string Item, TaskCompletionSource Completion)

还有另一种解决方案可能有效(但我真的建议改用 top-level 循环):您可以抑制逻辑上下文流(抑制 all AsyncLocal-样式值)。但是我会建议确保任务在让它逃离那个块之前在线程池线程上启动,。所以看起来像这样:

using (var activity = MyActivitySource.StartActivity("Parent"))
{
  using var suppressFlow = ExecutionContext.SuppressFlow();
  var taskStarted = new TaskCompletionSource(TaskCreationOptions.ExecuteContinuationsAsynchronously);
  task = Task.Run(async () =>
  {
    taskStarted.TrySetResult();
    var item = await channel.Reader.ReadAsync();
    Console.WriteLine("Task: {0}", Activity.Current?.DisplayName);
  });
}

但实际上,我认为放入“主循环”会更好。