Thursday, February 26, 2015

.Net ThreadPool (and the Task Parallel Library)

Intro

Threads are a great way to accomplish multiple tasks at the same time, while ensuring that you spread the joy among multiple cores on a multi-core system. They're excellent at improving the perceived response time of your applications and separating out disparate tasks to execute more quickly as a whole.

Even better, for a while now the .Net Framework has has a ThreadPool. This is a pool of background threads that are already created and ready to process things for you. Because they are already created, firing up one of these threads is a quicker process. They're performance-optimized threads ready for you to use with a minimal amount of code.

This blog assumes you have some knowledge of threaded programming in .Net. If you have no experience programming with threads, try this primer first: Multithreaded Programming Using C#.

Usage

Since .Net 4.0, the ThreadPool can be used with Generic tasks using the Task Parallel Library. I'd rather start off the explanation with an example so here we go:

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BlogThreadPool
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void ProcessFileGeneric(Object fileNumber)
        {
            var fileName = String.Format("file{0}.txt", fileNumber);
            if (File.Exists(fileName))
                File.Delete(fileName);
            File.WriteAllText(fileName, String.Format("file #{0} contents", fileNumber));
            Thread.Sleep(5000);
            File.AppendAllText(fileName, "\r\nsome more contents");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Task.Factory.StartNew(new Action<Object>(ProcessFileGeneric), 1);
            Task.Factory.StartNew(new Action<Object>(ProcessFileGeneric), 2);
            Task.Factory.StartNew(new Action<Object>(ProcessFileGeneric), 3);
        }
    }
}


This code simply creates 3 files and puts some content in them. The method ProcessFileGeneric is pretty self-explanatory. Delete a file if it exists, create the file and write some initial contents, wait 5 seconds, then append some more text to the file. The more interesting bit of magic is in button1_Click. This method creates 3 new tasks using the TPL, via Task.Factory.StartNew.Each call fires up one of the ThreadPool threads and feeds it a method to execute. In this case we are using the Action object and feeding it an Object parameter. Pretty cool huh?

As simple as that was, there's more to the ThreadPool than that. You might have already asked your computer "How many threads are sitting here waiting for my whimsy?". Well, there's an easy way to find out, and an easy way to set the value! See here:

        private void button2_Click(object sender, EventArgs e)
        {
            int maxWorkerThreads, completionPortThreads;
            ThreadPool.GetMaxThreads(out maxWorkerThreads, out completionPortThreads);
            int minWorkerThreads;
            ThreadPool.GetMinThreads(out minWorkerThreads, out completionPortThreads);
            int availableThreads;
            ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
            var data = String.Format(
                "MaxThreads: {0}\r\n" +
                "MinThreads: {1}\r\n" +
                "AvailableThreads: {2}\r\n",
                maxWorkerThreads, minWorkerThreads, availableThreads);
            MessageBox.Show(data);
        }


This cool new method shows us the usage of GetMaxThreads, GetMinThreads, and GetAvailableThreads. GetMaxThreads shows the max # of threads we can have active in the ThreadPool at any one time. GetMinThreads shows the minimum number that are awaiting our whims (including threads that are already working for us). GetAvailableThreads shows the # of unused threads available to us (excluding those that are already working for us). There are also setter methods SetMaxThreads and SetMinThreads. Under normal circumstances you won't use these much, but hey, you never know. If you run into performance issues with your ThreadPool code, play around with these and see what you get.

What's Next?

That's it for today folks. There is more to know about ThreadPool, background vs foreground threads, when you might want to use threads, and how to use them effectively in a desktop UI, but hey this blog can't go on forever. Check out the links in the Resources section below if you want to read more info. Happy learning!

 

Resources


The Managed Thread Pool
ThreadPool Class
Multithreaded Programming Using C#
Foreground and Background Threads

No comments:

Post a Comment