Could anyone please explain this, perhaps I'm missing something obvious.
These 2 cases seem to be identical in behavior, and yet they are not.
Case 1:
Start a Task with an
async
Action,
that does some work for some time:var t = Task.Run(async () => { await Task.Delay(2000); });
A second task waits for the first one:
var waitingTask = Task.Run(() => { t.Wait(); });
Wait for the second task:
waitingTask.Wait();
Case 2:
Build a
Task
using theTask
constructor, passing the sameasync
Action
:var t = new Task(async () => { await Task.Delay(2000); });
Start another task to Wait for the first one (just like in the first case):
var waitingTask = Task.Run(() => { t.Wait(); });
Start the first task:
t.Start();
Wait for the second task:
waitingTask.Wait();
The first case behaves as expected: the waiting task ends after the first one does, after 2 seconds.
The second case is weird: the waiting task ends very quickly, long before the first one does.
It's easy to see when printing messages from both tasks. A print at the end of the second task will show the difference.
I'm using VS 2015 Preview, which probably uses Roslyn to compile, if this matters.
It's discouraged to use a
Task
constructor withasync-await
, you should only be usingTask.Run
:The
Task
constructor was built before the [Task-based Asynchronous Pattern (TAP)][1] andasync-await
. It will return a task that just starts the inner task and moves on without waiting for it to complete (i.e. fire and forget).You could solve that by specifying your desired result as
Task<Task>
and useUnwrap
to create a task that represents the entire operation including the inner task:Using
Task.Run
is simpler and does all that for you, includingUnwrap
so you get back aTask
in return that represents the entireasync
operation.The answer is here http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
In effect the issue is that with
new Task
, and that it can only take aAction
, whilst you are trying to give aFunc<Task>
. Here are the first two lines again, but rewritten using the correct overloads.and
The first creates a task as expected. The second creates a task, who's return type is a task that waits 2000ms. The first example works because of the following overloads...
These overloads are designed to call
Task<Task<T>>.Unwrap()
automatically for you.The result is in your second example you are actually awaiting the start/enqueuing of the first task.
You can fix the second example by
Answer from Aron is not 100% correct. If you use a fix suggested, you will end up with:
In my case, I solved a similar problem by holding references to both wrapped and unwrapped tasks. You use
wrappedTask.Start()
to start the task, butunwrappedTask.Wait()
to await it (or to access anything related to task's execution, like itsTaskStatus
).(Source)