Today I want to cover the topic about how to limit the thread use by the Task library which is available in .NET 4.5. This post is related to my previous async and await syntax post.
First of all, do you know that when one Task start to run, a new thread will be spawn for that task? Task is supposed to be used for asynchronous operation, however, with its thread spawning behavior, it also can be used for multi-threading operation. More detail info, check out this Task Parallelism article.
There are 3 ways to create a Task:
Option 1:
Using Task.Factory.
Option 2:
Using Task.Start() method.
Option 3:
Using Task.Run() method.
First of all, do you know that when one Task start to run, a new thread will be spawn for that task? Task is supposed to be used for asynchronous operation, however, with its thread spawning behavior, it also can be used for multi-threading operation. More detail info, check out this Task Parallelism article.
There are 3 ways to create a Task:
Option 1:
Using Task.Factory.
Task
t1 = Task.Factory.StartNew(() =>
{
Console.WriteLine(DateTime.Now.ToString()
+ " - Task fired.");
Thread.Sleep(5000);
});
Option 2:
Using Task.Start() method.
Task
t2 = new Task(()
=>
{
Console.WriteLine(DateTime.Now.ToString()
+ " - Task fired.");
Thread.Sleep(5000);
});
t2.Start();
Option 3:
Using Task.Run() method.
Task
t3 = Task.Run(() =>
{
Console.WriteLine(DateTime.Now.ToString()
+ " - Task fired.");
Thread.Sleep(5000);
});
To me, the option 1 and 2 are the same, though the MSDN article suggested to use Factory when you do not want to separate the task creation and scheduling, but using Task explicitly also can do the job. May be Factory pattern look elegant? :P
Option 3 is different. The Task.Run() method only uses the available thread from the thread pool. Unlike option 1 and 2, both method spawn a new thread whenever a Task is started. Sometime, we need to limit the thread spawning because thread is resource expensive, it will consume all the memory and processing power that you have. The reason is the processor can only process one thing at a time, and due to the speed of the processor nowadays is extremely fast, it looks like all the tasks are processing concurrently. In fact, the processor is actually keep switching between threads to process bit by bit until the end and that created the illusion to many people think that their tasks are running concurrently. Even with the Hyper-Threading processor technology today, my Intel i7 4 cores 8 threads processor can only process 8 threads concurrently.
Therefore, we cannot set the maximum thread limit less than the logical processor count. For my processor, it has 8 logical processor, I am not allowed to set the maximum thread value less than 8. Also, the default minimum thread count is following logical processor count which is 8.
You can use the following method to check your current thread pool thread limit:
ThreadPool.GetMinThreads(out
workerThreadCount, out ioThreadCount);
ThreadPool.GetMaxThreads(out workerThreadCount, out ioThreadCount);
Then, change the thread pool limit with the following code:
//First parameter is
the number of thread in the pool
//Second parameter is
the number of async I/O thread in the pool
//Both value cannot
be less than your logical processor count
ThreadPool.SetMaxThreads(10, 10);
Then, combine with using Task.Run() method, you will notice only 10 threads will be spawn and working concurrently.
See the highlighted red box, there are 8 threads are actually running concurrently even I have set the max worker thread limit as 10. The other 2 thread spawn slightly later.
The following is the final piece of code:
class Program
{
static void
Main(string[] args)
{
int
workerThreadCount;
int
ioThreadCount;
ThreadPool.GetMinThreads(out
workerThreadCount, out ioThreadCount);
Console.WriteLine("Default
min worker thread: " + workerThreadCount.ToString());
Console.WriteLine("Default
min I/O thread: " + ioThreadCount.ToString());
ThreadPool.GetMaxThreads(out
workerThreadCount, out ioThreadCount);
Console.WriteLine("Default
max worker thread: " + workerThreadCount.ToString());
Console.WriteLine("Default
max I/O thread: " + ioThreadCount.ToString());
Console.ReadKey();
//First parameter is the
number of thread in the pool
//Second paramter is the
number of async I/O thread in the pool
//Both value cannot be less
than your logical processor count
ThreadPool.SetMaxThreads(10,
10);
ThreadPool.GetMinThreads(out
workerThreadCount, out ioThreadCount);
Console.WriteLine("Current
min worker thread: " + workerThreadCount.ToString());
Console.WriteLine("Current
min I/O thread: " + ioThreadCount.ToString());
ThreadPool.GetMaxThreads(out
workerThreadCount, out ioThreadCount);
Console.WriteLine("Current
max worker thread: " + workerThreadCount.ToString());
Console.WriteLine("Current
max I/O thread: " + ioThreadCount.ToString());
for
(int i = 1; i <= 100; i++)
{
//Task Factory
//Task t1 =
Task.Factory.StartNew(() =>
//{
// Console.WriteLine(DateTime.Now.ToString() +
" - Task fired.");
// Thread.Sleep(10000);
//});
//Normal Task Start
//Task t2 = new
Task(() =>
//{
// Console.WriteLine(DateTime.Now.ToString() +
" - Task fired.");
// Thread.Sleep(10000);
//});
//t2.Start();
//Thread Pool Task
Run
Task
t3 = Task.Run(() =>
{
Console.WriteLine(DateTime.Now.ToString()
+ " - Task fired.");
Thread.Sleep(10000);
});
}
Console.ReadKey();
}
}
Running the program long enough, you will see the 10 threads limit behavior since I purposely make every thread to sleep for 10 seconds. You can uncomment the code that use Factory and Task.Start(), you will notice they spawn threads uncontrollably.








