Task.Run method was introduced in .NET 4.5 (back in 2012 I think) to simplify the cases where one needs to offload some work to the thread pool. Previously we used
TaskFactory.StartNew method which has much more flexibility – and the inherent complexity. If not used correctly, the different parameter combinations could allow strange/bogus behaviors, such as accidentally starting a task on the UI thread.
Task.Run simplifies the general case, from time to time I find myself wondering which is happening under the hood. Using some decompiler we can what
Task.Run(Action action) is doing:
public static Task Run(Action action)
StackCrawlMark stackCrawlMark = StackCrawlMark.LookForMyCaller;
return Task.InternalStartNew(null, action, null,
InternalTaskOptions.None, ref stackCrawlMark);
So, it’s using:
TaskScheduler.Default– This ensures we’re offloading to the thread pool. One of the problems of not specifying the scheduler when using
Task.Factory.StartNewis that it uses
TaskScheduler.Current, which might be other than the default. In .NET 4.5 there’s also the
TaskCreationOptions.HideSchedulerflag, which results in
TaskScheduler.Defaultwithin the created task, even if it runs on a different scheduler
TaskCreationOptions.DenyChildAttach– This ensures that tasks created within the current task that specify
TaskCreationOptions.AttachToParentwon’t actually attach to anything because they won’t “see” the current task.
It’s worth pointing that the
Task.Run call above is equivalent to:
Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Both these arguments make sense as defaults and they are specially important when invoking external code. .NET folks actually admit they would make it the default if they could redo .NET 4.
You can find this and other useful information on the pfxteam blog; I really recommend reading it!