Stephen Toub blogged that
Both SynchronizationContext and TaskScheduler are abstractions that represent a “scheduler”, something that you give some work to, and it determines when and where to run that work. There are many different forms of schedulers. For example, the ThreadPool is a scheduler: you call ThreadPool.QueueUserWorkItem to supply a delegate to run, that delegate gets queued, and one of the ThreadPool’s threads eventually picks up and runs that delegate. Your user interface also has a scheduler: the message pump.
So System.Reactive.Concurrency.EventLoopScheduler
, Dispatcher, ThreadPool, TaskScheduler, SyncrhonizationContext, and IScheduler implementations of Reactive Extensions are all "schedulers" in that sense.
What is the difference between them?
Why were they all necessary? I think I get EventLoop, Dispatcher, ThreadPool. IScheduler are also well explained.
But TaskScheduler and SyncrhonizationContext still not clear to me.
Stephen Cleary's excellent article explains SyncrhonizationContext, and I think I get it. Why then we needed TaskScheduler, is not clear.
Please explain or point to a source.
I was just reading CLR via C#
book by Jeffrey Ritcher and thanks to him I can also give some easy explanation related to that topic. (assuming that I am not fully agreed with the whole details in answers)
First of all, TaskScheduler
object is responsible for executing scheduled tasks. The FCL ships with two TaskScheduler
-derived types: the thread pool task scheduler and a synchronization context task scheduler. By default, all applications use the
thread pool task scheduler. This task scheduler schedules tasks to the thread pool’s worker threads. You can get a reference to the default task scheduler by querying TaskScheduler
’s static Default
property.
The synchronization context task scheduler is typically used for applications sporting a graphical user interface. This task scheduler schedules all tasks onto the application’s GUI thread
so that all the task code can successfully update UI components like buttons, menu items, and so on.
The synchronization context task scheduler does not use the thread pool at all. You can get a reference to a synchronization context task scheduler by querying TaskScheduler
’s static FromCurrentSynchronizationContext
method.
As you can see from SynchronizationContextTaskScheduler
implementation, internally it uses SynchronizationContext
field. FCL
defines a base class, called System.Threading.SynchronizationContext
, which solves all these problems:
Simply stated, a SynchronizationContext
-derived object connects an application model to its threading model. The FCL defines several classes derived from
SynchronizationContext, but usually you will not deal directly with these classes; in fact, many of them are not publicly exposed or documented.
For the most part, application developers do not need to know anything about the SynchronizationContext
class. When you await
a Task
, the calling thread’s SynchronizationContext
object is obtained. When a thread pool thread completes the Task
, the SynchronizationContext
object is used, ensuring the right threading model for your application model. So, when a GUI thread
awaits
a Task
, the code following the await
operator is guaranteed to execute on the GUI thread as
well, allowing that code to update UI elements. For an ASP.NET application, the code following the
await operator is guaranteed to execute on a thread pool thread that has the client’s culture and
principal information associated with it.
You can, of course, define your own class derived from TaskScheduler
if you have special task
scheduling needs. Microsoft has provided a bunch of sample code for tasks and includes the source
code for a bunch of task schedulers in the Parallel Extensions Extras package. Like, IOTaskScheduler
, LimitedConcurrencyLevelTaskScheduler
, OrderedTaskScheduler
, PrioritizingTaskScheduler
, ThreadPerTaskScheduler
.
Each platform has it's own "scheduler" and they have their own abstractions around them. e.g. WinForms uses a message pump. WPF uses another message pump abstracted within "Dispatcher". A ThreadPool is another "scheduler" abstracted within "ThreadPool". These (and some others) are lower-level schedulers.
A Task and a TaskScheduler would like the user of a Task to not have to think about scheduling tasks at these lower levels (you can of course, in an abstracted way). You should be able to start a task and an ambient "scheduler" should take care of it. For example, TaskFactory.StartNew(()=>{LengthyOperation()})
should work regardless of what platform I'm running under. That's where a SynchronizationContext
comes in. It knows about what lower-level schedulers are involved in the currently running framework. That is passed along to a TaskScheduler
and that scheduler can both schedule tasks (possibly on to the ThreadPool) and schedule continuations through the lower-level scheduler associated with the currently running framework (see SynchronizationContext
) to maintain synchronization requirements. e.g. although you'd like your Task to run in the ThreadPool, you may want a continuation to run in the UI thread.
It's important to know that the TaskScheduler
is a abstraction of multiple other schedulers. This isn't the only reason it exists, but one of the reasons for this "extra" abstraction".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With