Task.Run vs. Task.Factory.StartNew (Part 1)
This post will focus on the key difference between Task.Run and Task.Factory.StartNew.
In general you have to know that Task.Run is more restricted version of Task.Factory.StartNew.
Task.Run sets default which fit for most common cases.
Task.Factory.StartNew is there for advance scenarios.
in general Task.Run is equivalent to:
Why should you care about it and how can it be used right?
I will start with the last default parameter, TaskScheduler.Default, which is used by Task.Run.
The default scheduler for Task.Factory.StartNew is actually TaskSheduler.Current.
TaskScheduler.Default will schedule the over the Thread Pool (by default).
When using Task.Factory.StartNew on operation that already running under different scheduler
you may be surprise by it behavior.
Take a look on the following code snippet:
At lien 13 it has resource which must be update from the UI thread.
At line 17 it register for event which raise on thread that is not the UI thread.
At line 23: the code schedule the execution of the event handling on the UI thread.
.NET component which need message pump mechanism (like the UI in WPF) is using .NET component
called SynchronizationContext, the scheduler at line 9 is using SynchronizationContext.Current for scheduling the work.
the method OnChangesUISafe will be scheduled on the UI thread (if the creation of the class will happen on the UI thread).
this way line 28 is safe.
The question is on which thread will the UpdateDatabase be scheduled?
At line 29 we use Task.Factory.StartNew to offload the work from the UI thread (in order to avoid freezing),
but is it really schedule the method on other thread rather the UI thread?
If so, on which thread will it schedule the work?
As I mention before the default for Task.Factory.StartNew is TaskScheduler.Current which mean in this case
that the task will be schedule over the SynchronizationContext i.e. the UI thread.
This code will actually cause UI freeze.
Task.Run come to help you avoid of this kind of mistakes.
Replacing line 17 with:
Will schedule the UpdateDatabase over a ThreadPool thread.
Is it always good to use Task.Run instead of Task.Factory.StartNew?
The answer is obviously not.
You should use it in most cases but sometime you want to go beyond its limitations.
A big issue on parallel programming is how to schedule IO operations?
the catch is that IO operation are relatively slow and don’t use the CPU (it may use
the network adapter or the hard disk controller but for most time the CPU is idle).
The problem start when people are using Thread from the thread pool in order to
preform synchronous IO operation in asynchronous way.
Why is It so bad?
It’s bad because the thread pool is optimize to do massive CPU bound operations and
when stilling one of its thread without using it for computation, it can become really slow
(I may write more of it on some other post).
What are our options?
The best option for executing IO operations in parallel is to await on method which
is using IO completion Port (for example HttpClient is using it when executing method like GetAsync, PostAsync, etc.).
But what if the provider don’t have a method that is using this technique?
When we schedule IO often we may consider using custom scheduler which will offload
the work to alternative (custom) pool which is using normal threads rather the thread pool.
When you have to schedule it for a few times you can simply
use TaskCreationOptions.LongRunning over the default Task Scheduler, it is kind of a hint for
the scheduler which make it schedule over new normal thread rather thread from the thread pool.
in either case this cannot be done by using Task.Run and you will have to choose Task.Factory.StartNew.
The following code snippet show simplified version of custom pool scheduling:
Using the Task Scheduler:
Using long running hint:
continue to part 2