Skip to content

Awaiting completed Task with Async never runs on only one thread #9685

@nike4613

Description

@nike4613

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

  1. 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
  1. 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
  1. 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);
        }
    }
}
  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions