In the following code, in the B method, the code Trace.TraceInformation("B - Started"); never gets called.
Should the method be running in parallel?
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
private static async Task A()
{
for (;;)
{
}
}
private static async Task B()
{
Trace.TraceInformation("B - Started");
}
static void Main(string[] args)
{
var tasks = new List<Task> { A(), B() };
Task.WaitAll(tasks.ToArray());
}
}
}
The
async
modifier is not a magic spawn-a-thread-here marker. It's sole purpose is to let the compiler know a method might depend on some asynchronous operation (A complex data-processing thread, I/O...) so it has to setup a state machine to coordinate the callbacks resulting from those asynchronous operations.To make
A
run on another thread you would invoke it using Task.Run which wraps the invocation on a new thread with a Task object, which you can await. Be aware thatawait
-ing a method does not mean your code runs in parallel toA
's execution all by itself: It will until the very line youawait
the Task object, telling the compiler you need the value that the Task object returns. In this caseawait
-ingTask.Run(A)
will effectively make your program run forever, waiting forA
to return, something that will never happen (barring computer malfunction).Do have in mind that marking a method as async but not actually awaiting anything will only have the effect of a compiler warning. If you await something that is not truly async (It returns immediately on the calling thread with something like Task.FromResult) it will mean your program takes a runtime speed penalty. It is very slight, however.
Short answer
No, as you wrote your two
async
methods, they are indeed not running in parallel. Addingawait Task.Yield();
to your first method (e.g. inside the loop) would allow them to do so, but there are more reasonable and straightforward methods, highly depending on what you actually need (interleaved execution on a single thread? Actual parallel execution on multiple threads?).Long answer
First of all, declaring functions as
async
does not inherently make them run asynchronously or something. It rather simplifies the syntax to do so - read more about the concepts here: Asynchronous Programming with Async and AwaitEffectively
A
is not asynchronous at all, as there is not a singleawait
inside its method body. Instructions up to the first use ofawait
run synchronously like a regular method would.From then on, the object that you
await
determines what happens next, i.e. the context that the remaining method runs in.To force execution of a task to happen on another thread, use
Task.Run
or similar.In this scenario, adding
await Task.Yield()
does the trick since the current synchronization context isnull
and this happens to indeed cause the task scheduler (should beThreadPoolTaskScheduler
) to execute the remaining instuctions on a thread-pool thread - some environment or configuration might cause you to only have one of them, so things would still not run in parallel.Summary
The moral of the story is: Be aware of the differences between two concepts:
async
/await
reasonably) andTask.Run
,Thread
, etc. in which case the use ofasync
is completely irrelevant anyway)No, the methods shown are not expected to "run in parallel".
Why
B
is never called - you have list of taskstasks
constructed via essentially series of.Add
calls - and first is result ofA()
is added. Since theA
method does not have anyawait
it will run to the completion synchronously on the same thread. And after thatB()
would be called.Now
A
will never complete (it is sitting in infinite loop) so really code will not even reach call toB
.Note that even if creation would succeed code never finish
WaitAll
asA
still sits in infinite loop.If you want methods to "run in parallel" you need to either run them implicitly/explicitly on new threads (i.e. with
Task.Run
orThread.Start
) or for I/O bound calls let method to release thread withawait
.