This diagram might help spell that out for you a bit better:Īs you can see, if we aren’t switching between pieces of work, then we don’t have the context switches between threads. Each time that the CPU has to switch from thread to thread causes a bit of overhead, and if you have many threads running at once, then this switching can happen quite often causing the work to take longer than if it had just been executed synchronously. You see, threading has overhead, and if you are trying to execute more CPU bound threads on a machine than you have available cores for them to run, then you can possibly run into problems. For example, if I am going to fire off multiple threads that are all going to be doing heavy CPU bound work, then on a single core machine we are likely to cause the work to take significantly longer.
So, how is this different from creating a thread again? Well, one of the first advantages of using Tasks over Threads is that it becomes easier to guarantee that you are going to maximize the performance of your application on any given system. This way we now have a reference to the task. Hey, that is pretty simple, and it doesn’t look too far removed from throwing items on the thread pool! In fact, when we execute this line, we really are just dropping a task on the thread pool because we aren’t getting a reference to the task so that we can use it’s extra functionality! To do this, we could simply assign the result to a variable: var task = (() => DoSomeWork()) Let’s look at how we could create one of these tasks: (() => DoSomeWork())
And what if we wanted to specify some piece of code that would execute directly after that queued work, and then would use the result? Or what if we wanted to fire off a few pieces of work and then wait for all of them to finish before continuing? Or what if we only wanted to wait for just one of them to finish? What if we wanted to return some value from the piece of work, but block if the result was requested before it was available? What about all of those things? A bit daunting, right? Well, all of this functionality is exactly why Tasks in. In order to wait, we could have done something like this: var mre = new ManualResetEvent(false) īut that is just a tad bit ugly. For example, in the code above, what would we have had to do in order to wait on that piece of work to finish? The thread pool doesn’t give us any built-in way to do this, it is just fire and forget. NET, when we put an item on the thread pool, we had a very hard time getting any information back about what exactly was going on with the piece of work that we had just queued. Okay, so if all we are doing is just plopping a new task on the thread pool, then why do we need this new Task namespace? Well, I’m glad you asked! In previous versions of. NET 2.0 when we said: ThreadPool.QueueUserWorkItem(_ => DoSomeWork()) So, creating a new task is similar to what we did in. NET 4.0 don’t actually correlate to a new thread, they are executed on the new thread pool that is being shipped in. The Tasks in namespace are a method of fine grained parallelism, similar to creating and using threads, but they have a few key differences. One of these tools is a new namespace within the System.Threading namespace which is called "Tasks". NET 4.0 framework and Visual Studio 2010 we are going to get a plethora of new tools to help us write better multi-threaded applications.