-
Notifications
You must be signed in to change notification settings - Fork 847
Description
When using Async.AwaitTask on a Task that is already completed (like from Task.FromResult), the continuation is never executed synchronously on the invoking thread.
Repro steps
- Compile and run this F# program:
open System
open System.Threading
open System.Threading.Tasks
[<EntryPoint>]
let main argv =
async {
printfn "Starting on thread %d" Thread.CurrentThread.ManagedThreadId
let! value = Task.FromResult(42) |> Async.AwaitTask
printfn "Got value %d on thread %d" value Thread.CurrentThread.ManagedThreadId
return 0
} |> Async.RunSynchronously- Observe that the thread the continuation runs on is different than the thread the start runs on in F#
Starting on thread 1
Got value 42 on thread 4
- Compile and run this equivalent C# program:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskTesting
{
class Program
{
public static async Task Main(string[] args)
{
Console.WriteLine("Starting on thread {0}", Thread.CurrentThread.ManagedThreadId);
var value = await Task.FromResult(42);
Console.WriteLine("Got value {0} on thread {1}", value, Thread.CurrentThread.ManagedThreadId);
}
}
}- Ovserve that the thread that the continuation runs on is the same as the thread the start runs on in C#
Starting on thread 1
Got value 42 on thread 1
Expected behavior
When the task is already completed, the entire block runs on a single thread.
Actual behavior
The continuation after the task is always scheduled to run on the thread pool (or presumably whatever scheduler is currently available).
Known workarounds
I know of no workarounds for this.
Related information
Running .NET 5 Preview 5 and 6 on Windows through Visual Studio.
After having stepped through a whole bunch of the async code trying to figure out why it happens, I think its because the call to Task.ContinueWith does not have the RunSynchronously flag, and so the continuation is never run on the same thread, but instead sent off to the thread pool even when it doesn't need to. The C# state machine seems to deal with this by always checking whether or not it is complete before registering the continuation, and if it is complete, just executing the continuation on the spot.