Reputation: 1236
Consider this code snippet:
Task[] tasks = new Task[4];
for (var i = 0; i < tasks.Length; ++i) {
tasks[i] = Task.Run(async () =>
{
await Task.Delay(4000);
});
}
for (var i = 0; i < tasks.Length; ++i)
await tasks[i];
Console.WriteLine("Done!");
This works as expected, taking 4.000 ms to execute. However, if I exchange Task.Run
with Task.Factory.StartNew
it takes only 0.006 ms !
Can anyone explain why?
Upvotes: 10
Views: 7825
Reputation: 456527
Can anyone explain why?
Put simply, StartNew
does not understand async
delegates.
So, when your delegate returns an incomplete task at its first await
, StartNew
sees the delegate exit and considers its work complete. This is why it returns a Task<Task>
here. Task.Run
has special logic to handle asynchronous delegates, automatically unwrapping the inner task.
This is only one of the pitfalls of using StartNew
with asynchronous code; I cover this and the others in detail in my blog post StartNew
is Dangerous.
Upvotes: 10
Reputation: 3839
The answer is here
there is a difference in behavior between the two methods regarding : Task.Run(Action) by default does not allow child tasks started with the TaskCreationOptions.AttachedToParent option to attach to the current Task instance, whereas StartNew(Action) does
So, the Task.Run
will wait while execution will finish and the Task.Factory.StartNew
return a task immediately.
Upvotes: 3
Reputation: 1236
Ok, I found the answer myself after reading this https://blogs.msdn.microsoft.com/pfxteam/2011/10/24/task-run-vs-task-factory-startnew/
By using the async keyword here, the compiler is going to map this delegate to be a
Func<Task<int>>
: invoking the delegate will return theTask<int>
to represent the eventual completion of this call. And since the delegate isFunc<Task<int>>
,TResult
isTask<int>
, and thus the type of ‘t’ is going to beTask<Task<int>>
, notTask<int>
.
So this code works as expected:
Task[] tasks = new Task[4];
for (var i = 0; i < tasks.Length; ++i) {
tasks[i] = Task.Factory.StartNew(async () =>
{
await Task.Delay(4000);
});
}
for (var i = 0; i < tasks.Length; ++i)
await await (tasks[i] as Task<Task>);
Console.WriteLine("Done!");
Which can be implemented also using Unwrap
:
Task[] tasks = new Task[4];
for (var i = 0; i < tasks.Length; ++i) {
tasks[i] = Task.Factory.StartNew(async () =>
{
await Task.Delay(4000);
}).Unwrap();
}
for (var i = 0; i < tasks.Length; ++i)
await tasks[i];
Console.WriteLine("Done!");
Upvotes: 8