{"id":253,"date":"2013-06-06T23:31:43","date_gmt":"2013-06-06T23:31:43","guid":{"rendered":"http:\/\/www.silverclaw.net\/?p=253"},"modified":"2013-06-06T23:31:43","modified_gmt":"2013-06-06T23:31:43","slug":"asp-net-multi-threading-thread-vs-threadpool-vs-task-vs-parallel-which-one-is-the-best-choice","status":"publish","type":"post","link":"https:\/\/www.silverclaw.net\/?p=253","title":{"rendered":"ASP.NET multi-threading: Thread vs ThreadPool vs Task vs Parallel. Which one is the best choice?"},"content":{"rendered":"<p>I&#8217;m currently working on my upcoming ASP.NET 4.5 Expert Skills book, and it struck me that there are now at least 4 different options available to the programmer when implementing multi-threading. If you&#8217;re not already familiar with these classes, choosing between them is likely to be extremely difficult.<\/p>\n<p>Let&#8217;s take some simple pseudo-code as an example:<\/p>\n<pre>foreach (var FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 EncryptFile(FileToEncrypt);\r\n}<\/pre>\n<p>In this example, let&#8217;s imagine that the <em>EncryptFile<\/em> method is very processor-intensive, so we&#8217;d like to split the calls to <em>EncryptFile<\/em> into separate threads.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Using the Thread class<\/strong><\/span><\/p>\n<p>The <em>Thread<\/em> class has been around since the original release of the .NET framework, so\u00a0 it&#8217;s likely that existing multi-threading code will use this class.<em><\/em><\/p>\n<p>The <em>Thread<\/em> class is found in the <em>System.Threading<\/em> namespace.<\/p>\n<p>Here&#8217;s how the multi-threaded code would look using the <em>Thread<\/em> class:<\/p>\n<pre>List&lt;Thread&gt; MyThreads = new List&lt;Thread&gt;();\r\nforeach (var FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 Thread NewThread = new Thread(new ParameterizedThreadStart(FileToEncrypt));\r\n\u00a0\u00a0\u00a0 NewThread.Start(FileToEncrypt);\r\n}<\/pre>\n<p>This looks fine as long as it is all that your program needs to do. But let&#8217;s assume that we need to do something with the encrypted files after the threads have finished encrypting them. We&#8217;ll need to add some code that waits until all of the threads have finished before continuing:<\/p>\n<pre>List&lt;Thread&gt; Threads = new List&lt;Thread&gt;();\r\nforeach (string FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 Thread NewThread = new Thread(new ParameterizedThreadStart(FileToEncrypt));\r\n\u00a0\u00a0\u00a0 Threads.Add(NewThread);\r\n\u00a0\u00a0\u00a0 NewThread.Start();\r\n\u00a0\u00a0\u00a0 Stuff(FileToEncrypt);\r\n}\r\nbool AllThreadsFinished = false;\r\nwhile (!AllThreadsFinished)\r\n{\r\n\u00a0\u00a0\u00a0 AllThreadsFinished = true;\r\n\u00a0\u00a0\u00a0 foreach (Thread MyThread in Threads)\r\n\u00a0\u00a0\u00a0 {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 if (MyThread.IsAlive) AllThreadsFinished = false;\r\n\u00a0\u00a0\u00a0 }\r\n\u00a0\u00a0\u00a0 Thread.Sleep(1000);\r\n}<\/pre>\n<p>We&#8217;ve had to add quite a lot of code to achieve it, but this code will now wait for all of the threads to finish. This isn&#8217;t the only way to achieve this using the <em>Thread<\/em> class, but it&#8217;s the easiest to understand and uses the least code.<\/p>\n<p>The <em>Thread<\/em> class does a good job of splitting code into multiple threads, and will even benefit from the improvements to threading that were added to ASP.NET 4.5. If your existing code uses the <em>Thread<\/em> class, there&#8217;s really no need to change it.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Using the ThreadPool class<\/strong><\/span><\/p>\n<p>The <em>ThreadPool<\/em> class is another class that has been around since the first version of the .NET framework. <em>ThreadPool<\/em> offers more control over how threads are distributed to the computers processor or processors, but it is much harder to implement the &#8216;wait&#8217; code that we used for the <em>Thread<\/em> class.<\/p>\n<p>The <em>ThreadPool<\/em> class is found in the <em>System.Threading<\/em> namespace.<\/p>\n<p>Here&#8217;s how our threading code would look using the <em>ThreadPool<\/em> class:<\/p>\n<pre>foreach (string FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 ThreadPool.QueueUserWorkItem(new WaitCallback(EncryptFile), FileToEncrypt);\r\n}<\/pre>\n<p>This looks much easier, but the <em>ThreadPool<\/em> class doesn&#8217;t give us any way to check on the status of the thread that we just created, so how can we tell when it has finished?<\/p>\n<p>There are some very complicated ways of extracting the status of the thread, but an alternative option is to maintain a counter that is incremented when the <em>EncryptFile<\/em> method finishes. We&#8217;ll call it: <strong>NumberOfFilesEncrypted<\/strong><\/p>\n<p><strong><\/strong>We can then wait for all of the threads to finish by using the following code:<\/p>\n<pre>foreach (string FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 ThreadPool.QueueUserWorkItem(new WaitCallback(EncryptFile), FileToEncrypt);\r\n}\r\nwhile (NumberOfFilesEncrypted &lt; FilesToEncrypt.Count)\r\n{\r\n\u00a0\u00a0\u00a0 Thread.Sleep(1000);\r\n}<\/pre>\n<p>Of course, this is far from ideal. If one of the threads failed for some reason, the code would wait forever for the last thread to finish. This could be worked around with error handling and timeouts, but it&#8217;s really better to avoid the <em>ThreadPool<\/em> class if possible.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Using the new Task class<\/strong><\/span><\/p>\n<p>The <em>Task<\/em> class was introduced in .NET 4.0, but has only really been finalized in the .NET 4.5 release. The <em>Task<\/em> class combines the functionality of the <em>Thread<\/em> and <em>ThreadPool<\/em> classes to make threading easier and more efficient.<\/p>\n<p>The <em>Task<\/em> class is found in the new <em>System.Threading.Tasks<\/em> namespace.<\/p>\n<p>Here&#8217;s how our code would look using the <em>Task<\/em> class:<\/p>\n<pre>foreach (string FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 Task.Factory.StartNew(Argument =&gt; EncryptFile(Argument), FileToEncrypt);\r\n}<\/pre>\n<p>Once again, of course, we need to check whether all of the threads have finished before continuing. We can do this in a very similar way to the <em>Thread<\/em> class, but with much less code:<\/p>\n<pre>List&lt;Task&gt; MyTasks = new List&lt;Task&gt;();\r\nforeach (string FileToEncrypt in FilesToEncrypt)\r\n{\r\n\u00a0\u00a0\u00a0 MyTasks.Add(Task.Factory.StartNew(Argument =&gt; EncryptFile(Argument), FileToEncrypt));\r\n}\r\nTask.WaitAll(MyTasks.ToArray());<\/pre>\n<p>The <em>Task.WaitAll<\/em> method makes everything much easier! If you have the choice between the <em>Thread<\/em> and <em>Task<\/em> classes, I&#8217;d recommend the <em>Task<\/em> class every time.<\/p>\n<p>However, I&#8217;ve saved the best for last. The <em>Parallel <\/em>class enables us to carry out the task with even less code.<\/p>\n<p><span style=\"text-decoration: underline;\"><strong>Using the new Parallel class<\/strong><\/span><\/p>\n<p>The <em>Parallel<\/em> class enables you to change any <em>for<\/em> or <em>foreach<\/em> loop into multi-threaded code. Every iteration of the loop will be intelligently assigned to a separate thread and will be executed in the most efficient way possible.<\/p>\n<p>The <em>Parallel<\/em> class can be found in the <em>System.Threading.Tasks<\/em> namespace.<\/p>\n<p>Here&#8217;s how our code would look using the <em>Parallel<\/em> class:<\/p>\n<pre>Parallel.ForEach(FilesToEncrypt, FileToEncrypt =&gt;\r\n{\r\n\u00a0\u00a0\u00a0 EncryptFile(FileToEncrypt);\r\n});<\/pre>\n<p>You might be wondering how we wait for all of the threads to finish. In fact, the<em> Parallel<\/em> class handles this automatically! The <em>foreach<\/em> loop won&#8217;t finish until all of the threads have finished running.<\/p>\n<p>If the code that you want to be multi-threaded can be expressed as a <em>for<\/em> or <em>foreach<\/em> loop, there&#8217;s little doubt that you want to use the <em>Parallel<\/em> class.<\/p>\n<p>If you&#8217;re using ASP.NET 4.5, I would highly recommend using the <em>Task<\/em> class and <em>Parallel<\/em> classes and ignoring the older <em>Thread<\/em> and <em>ThreadPool<\/em> classes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m currently working on my upcoming ASP.NET 4.5 Expert Skills book, and it struck me that there are now at least 4 different options available to the programmer when implementing multi-threading. If you&#8217;re not already familiar with these classes, choosing &hellip; <a href=\"https:\/\/www.silverclaw.net\/?p=253\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[12],"tags":[],"_links":{"self":[{"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/posts\/253"}],"collection":[{"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=253"}],"version-history":[{"count":11,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/posts\/253\/revisions"}],"predecessor-version":[{"id":266,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=\/wp\/v2\/posts\/253\/revisions\/266"}],"wp:attachment":[{"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.silverclaw.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}