await
does not guarantee continuation on the same task for spawned tasks:
private void TestButton_Click(object sender, RoutedEventArgs e)
{
Task.Run(async () =>
{
Debug.WriteLine("running on task " + Task.CurrentId);
await Task.Delay(TimeSpan.FromMilliseconds(100));
Debug.WriteLine("running on task " + Task.CurrentId);
});
}
The output of this is:
running on task 1
running on task
so we can see that not only the execution has moved to another task, but also to the UI-thread. How can i create a dedicated task, and enforce await to always continue on this task? Long-running tasks don't do this either.
I have seen several SynchronizationContext implementations, but so far none of them worked, in this case because it uses threads and System.Threading.Thread is not available for uwp.
so we can see that not only the execution has moved to another task, but also to the UI-thread.
No, it's not on the UI thread. It's just technically not on a task, either. I explain why this happens in my blog post on Task.CurrentId
in async
methods.
How can i create a dedicated task, and enforce await to always continue on this task? Long-running tasks don't do this either.
You're on the right track: you need a custom SynchronizationContext
(or a custom TaskScheduler
).
I have seen several SynchronizationContext implementations, but so far none of them worked, in this case because it uses threads and System.Threading.Thread is not available for uwp.
Try out mine. It should work on UWP 10.0.
Don't use Task.Run
, just make the event handler async
private async void TestButton_Click(object sender, RoutedEventArgs e)
{
await dedicated();
}
private async Task dedicated()
{
Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
await Task.Delay(TimeSpan.FromMilliseconds(100));
Console.WriteLine("running on task {0}", Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null");
}
Read more about Task.CurrentId
in Async Methods here:
So Task.CurrentId
returns null
because there is no task actually
executing.
Reply to comments
- "It still runs on the UI thread, and not a spawned task."
The case with a thread pool thread is included in the link. In particular look at this example
static void Main(string[] args)
{
var task = Task.Run(() => MainAsync());
task.Wait();
taskRun = task.Id.ToString();
Console.WriteLine(beforeYield + "," + afterYield + "," + taskRun);
Console.ReadKey();
}
static async Task MainAsync()
{
beforeYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
await Task.Yield();
afterYield = Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null";
}
Again, the clarification is
the null
comes into play because the async
method is first executed as
an actual task on the thread pool. However, after its await
, it
resumes as a regular delegate on the thread pool (not an actual task).
- "my question is how to stop it from doing that"
This is an implementation detail of the async
call, I can only quote the link again:
It’s likely that this behavior is just the result of the easiest and
most efficient implementation.
So you can't and you should not stop it from doing that, as far as a truly async
call is concerned.
What you describe as the expected behavior instead is equivalent to a Task.Run
without await
private void expected()
{
Task task = Task.Run(() =>
{
Console.WriteLine("Before - running on task {0} {1}",
Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
Environment.CurrentManagedThreadId);
Task.Delay(TimeSpan.FromMilliseconds(100)).Wait();
Console.WriteLine("After - running on task {0} {1}",
Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
Environment.CurrentManagedThreadId);
});
}
Or a nested Task.Run
private void expected()
{
Task task = Task.Run(() =>
{
Console.WriteLine("Before - running on task {0} {1}",
Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
Environment.CurrentManagedThreadId);
var inner = Task.Run( async () =>
await Task.Delay(TimeSpan.FromMilliseconds(100)));
inner.Wait();
Console.WriteLine("After - running on task {0} {1}",
Task.CurrentId.HasValue ? Task.CurrentId.ToString() : "null",
Environment.CurrentManagedThreadId);
});
}
Output
Before - running on task 312 11
After - running on task 312 11
Before - running on task 360 11
After - running on task 360 11
Before - running on task 403 15
After - running on task 403 15