Scheduling Task vs. Task Creation
.net, async-await, asynchronous, c#, multithreading
Solution
You can specify the `TaskScheduler` to use in certain overloads of `ContinueWith`. You decide where to run that code. It is not true that the scheduler cannot be specified here.
An async method runs on the captured `SynchronizationContext` or on the current `TaskScheduler` after the first await point. So indeed an async method does schedule continuations. (I'm leaving out the fact that you can have custom awaiters.)
Your async example synchronously runs on the main thread to completion.
Is it correct that ContinueWith not only creates the task but also schedules it.
Yes, on the scheduler you specify.
Is it correct that await not only creates the task for continuation as appears in the article but also schedule if possible (Call Post on SynchronizationContext or TaskScheduler).
Yes, after the first await (that does not immediately complete) a scheduling operation happens.
Why this design (scheduling and creation mixed) was adopted by async/await language designers?
The internals of async/await are quite involved. There is a lot of machinery and non-trivial behavior under the hood. It is especially surprising and inconsistent on what thread the code of an async method will actually run. The designers of this feature apparently had a hard time making this work out of the box in all important scenarios. This leads to numerous questions on Stack Overflow every day about edge cases and very surprising behavior.
You can untangle creation and scheduling with the rarely-used `Task.Start (TaskScheduler)` method.
For continuations this model doesn't work out. When the antecendent completes the continuation must be activated. Without a `TaskScheduler` to do that the continuation cannot be run. That's why the scheduler must be specified at the time the continuation is being set up.
For async methods you can untangle creation and scheduling as well by using a custom awaiter. Or by using simpler models such as `await Task.Factory.StartNew(..., myScheduler)`.
bar will be executed without specifically scheduling the Task object created by foo.
This task is not a CPU-based task. It is never scheduled. This is a task backed by a `TaskCompletionSource`. Specifying a scheduler doesn't make sense here.
Problem
I am quite confused with the subject. I am coming from assumption that task creation and its scheduling should be strictly separated which seams not to be the case in C#. Consider the following simple code. ``` static async Task simpleton() { print("simpleton"); } static async Task cont() { print("cont"); } static void Main(string[] args) { Task t1 = simpleton(); Task t2 = t1.ContinueWith((a) => { cont(); }); Thread.Sleep(1000); return; } ``` The output is ``` simpleton cont ``` simpleton function runs and creates the task t1 (already completed) - that's ok. However the t2 seams to be (at least in code) only task creation - no scheduling was asked from the system, so why and who's schedules the continuation to run? Clearly the creation/running principle is broken here. Similar situation is with await. Consider the pseudo code in this very well known article. According to pseudo code await is translated into the continuation task and returned to the caller, which I would expect must schedule the task in order to complete it . Also this is not the case, consider the following code:- ``` static async Task foo() { await bar(); } static async Task bar() { print("bar"); } static void Main(string[] args) { foo(); Thread.Sleep(1000); return; } ``` bar will be executed without specifically scheduling the Task object created by foo. So the questions are: Is it correct that ContinueWith not only creates the task but also schedules it . Is it correct that await not only creates the task for continuation as appears in the article but also schedule if possible (Call Post on SynchronizationContext or TaskScheduler). Why this design (scheduling and creation mixed) was adopted by async/await language designers?