I have the following code:
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;
I would like to start the task without injecting Activity. I cannot create the task outside the using(var acrivity...).
One option (I suppose) is setting Activity.Current = null at the beginning of the task. Is there an alternative option?
One option (I suppose) is setting Activity.Current = null at the beginning of the task. Is there an alternative option?
My preferred solution would be to move the "processing" code into a separate top-level loop. I.e., move the code currently within StartNew
into a completely separate method, wrap it in an await foreach (var item in channel.Reader.ReadAllAsync())
, and start that loop at the time you start your channel. Thus essentially making it an ActionBlock
of sorts.
You may need to augment your current string
item with other values to make that work. I.e., if you need per-item completion, then you can replace string
with (string Item, TaskCompletionSource Completion)
.
There is another solution that may work (but I really recommend using the top-level loop instead): you can suppress the logical context flow (which suppresses all AsyncLocal
-style values). But then I would recommend ensuring the task has started on the thread pool thread before letting it escape that block, just in case some code waits synchronously. So that would look like this:
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);
});
}
But really, I think putting in a "main loop" would be better.