I’ve been asked so many times to address this topic but as you may already know it’s a bit complex to say the least and needs to be covered extensively since you can find use cases roughly everywhere. Threads are usually managed by a thread scheduler that ensures that all active threads are given CPU time and the threads that are waiting or blocked (for instance waiting for a resource to be freed or waiting for user input) do not consume any CPU time.
Typical use cases are those when you want to do multiple things at the same time, such as time expensive IO operations(download file, write in a file, read from database etc) or image processing etc and maintaining the rest of your application responsive. For example UI applications or servers are great examples for heavy thread usage. Now, as good as threads may be they can pose serious problems such as locking and memory leaks especially when used incorrectly. As I said earlier one of the most common issue is data sharing between threads or in other words how do the threads access a common resource without ending up in a deadlock. A deadlock is a situation where two(or more threads) are waiting for the other(s) to finish it’s job with the resource and as such it never does. So you have to be really careful when you’re sharing data between threads typically using a lock mechanism such as monitors, mutex or a semaphore to prevent that from happening. As for collections, C# provides you with thread safe collections: queues, stacks, dictionaries, you name it which all reside under
System.Collections.Concurrent namespace which it is a topic by itself that I’ll cover some other time.
Relevant namespaces for working with threads are System.Threading and System.Threading.Tasks. The first one is the old way of dealing with threading and it has been a while from .NET framework 2.0 while the latter one has been around only from .NET 4.0 and it somewhat simplifies threading. And then there is Task Parallel Library which is now the standard way.
Thread pool recycles threads and manages the number of threads that can be run in total such that once the maximum limit is reached, the jobs are queued and executed once a thread is freed. The Task Asynchronous Pattern(TAP) is an advanced and rather new mechanism that uses threads very efficiently and thus making applications much faster.
Using TPL(Task Parallel Library) starting new threads is extremely easy and resumes to a call like the one bellow:
[code lang=”csharp”]Task.Factory.StartNew(() => { Console.WriteLine("Hello"); });[/code]
Ofcourse I made use of lambdas there but basically you can use any delegate or method for that matter. Sure, you may ask yourself what if the method signature is not void, what if I want to get back some result. Sure you can do that:
[code lang=”csharp”]
// The <string> type argument for Task.Factory.StartNew
// is not necessary as it is inferred and you can remove it.
// Start the task executing:
Task<string> task = Task.Factory.StartNew<string>(() => GetMyCustomer());
// We can do other work here and it will execute in parallel:
RunSomethingElse();
// When we need the task’s return value, we query its Result property:
// If it’s still executing, the current thread will now block (wait)
// until the task finishes:
string customer = task.Result;
[/code]
Any unhandled exceptions are automatically rethrown when you query the task’s Result property and are wrapped in an AggregateException. However, if you fail to query its Result property (and don’t call Wait on the task) any unhandled exception will take the process down. You can find an example on how to handle such situations bellow:
[code lang=”csharp”]
Task<string> task = Task.Factory.StartNew(() => GetMyCustomer());
try
{
task.Wait();
}
catch (AggregateException aex)
{
foreach (Exception ex in aex.InnerExceptions)
Console.WriteLine(ex.Message);
}
[/code]
Alright, so you’ve learned some basics about threading in C# will continue this series as following:
Part II – More usages of threading(Timers, Dispatchers)
Part III – Concurrency, Concurrent Collections
Part IV – Task Asynchronous Pattern(TAP)