7
votes

In the context of a console application making use of async/await constructs, I would like to know if it's possible for "continuations" to run in parallel on multiple threads on different CPUs.

I think this is the case, as continuations are posted on the default task scheduler (no SynchronizationContext in console app), which is the thread pool.

I know that async/await construct do not construct any additional thread. Still there should be at least one thread constructed per CPU by the thread pool, and therefore if continuations are posted on the thread pool, it could schedule task continuations in parrallel on different CPUs ... that's what I thought, but for some reason I got really confused yesterday regarding this and I am not so sure anymore.

Here is some simple code :

public class AsyncTest
{
  int i;

  public async Task DoOpAsync()
  {
    await SomeOperationAsync();

    // Does the following code continuation can run 
    // in parrallel ?
    i++;       

    // some other continuation code ....
  }

  public void Start()
  {
    for (int i=0; i<1000; i++)
    { var _ = DoOpAsync(); } // dummy variable to bypass warning
  }
}

SomeOperationAsync does not create any thread in itself, and let's say for the sake of the example that it just sends some request asynchronously relying on I/O completion port so not blocking any thread at all.

Now, if I call Start method which will issue 1000 async operations, is it possible for the continuation code of the async method (after the await) to be run in parallel on different CPU threads ? i.e do I need to take care of thread synchronization in this case and synchronize access to field "i" ?

2
Do you want them to be run in parallel, or not? If you do, it's easy enough to synchronize all of the calls, just await DoOpAsync in Start.Servy

2 Answers

6
votes

Yes, you should put thread synchronization logic around i++ because it is possible that multiple threads would be executing code after await at the same time.

As a result of your for loop, number of Tasks will be created. These Tasks will be executed on different Thread Pool threads. Once these Tasks are completed the continuation i.e. the code after the await, will be executed again on different Thread Pool threads. This makes it possible that multiple threads would be doing i++ at the same time

6
votes

Your understanding is correct: in Console applications, by default continuations will be scheduled to the thread pool due to the default SynchronizationContext.

Each async method does start synchronously, so your for loop will execute the beginning of DoOpAsync on the same thread. Assuming that SomeOperationAsync returns an incomplete Task, the continuations will be scheduled on the thread pool.

So each of the invocations of DoOpAsync may continue in parallel.