Delegates are a few of the objects that make threading easier in .NET reference. They can be used to asynchronously invoke a method. What other objects exist in framework 4.5 (or earlier) that make the use of threads easier or less error prone?
What are the other abstractions make concurrency and multithreading easier?
Note: This question updates this.
I tend to answer a lot of questions related to multithreading and I often see the same basic question asked in various different ways. I will present the most common problems as I have seen them over the years and explain how the newer technologies have made solving these problems easier.
Closing over the loop variable
This is not a problem specific to threading, but the use of threading definitely magnifies the problem. C# 5.0 fixes this problem for the
foreachloop by creating a new variable for each iteration. You will no longer have to create a special variable for lambda expression closures. Unfortunately, theforloop will still need to be handle with a special capturing variable.Waiting for asynchronous tasks to complete
.NET 4.0 introduced the
CountdownEventclass which encapsulates a lot of the logic required to wait for the completion of many tasks. Most junior developers usedThread.Joincalls or a singleWaitHandle.WaitAllcall. Both of these have scalability problems. The old pattern was to use a singleManualResetEventand signal it when a counter reached zero. The counter was updated using theInterlockedclass.CountdownEventmakes this pattern much easier. Just remember to treat your main as a worker as well to avoid that subtle race condition that can occur if one worker finishes before all workers have been queued..NET 4.0 also introduced the
Taskclass which can have child tasks chained off of it viaTaskCreationOptions.AttachedToParent. If you callTask.Waiton a parent it will wait for all child tasks to complete as well.Producer-Consumer
.NET 4.0 introduced the
BlockingCollectionclass which acts like a normal queue except that it can block when the collection is empty. You can queue an object by callingAddand dequeue an object by callingTake.Takeblocks until an item is available. This simplifies producer-consumer logic considerably. It used to be the case that developers were trying to write their own blocking queue class. But, if you do not know what you are doing then you can really screw it up…bad. In fact, for the longest time Microsoft had a blocking queue example in the MSDN documentation that was itself badly broken. Fortunately, it has since been removed.Updating UI with worker thread progress
The introduction of
BackgroundWorkermade spinning off a background task from a WinForm application a lot easier for novice developers. The main benefit is that you can callReportProgressfrom within theDoWorkevent handler and theProgressChangedevent handlers will be automatically marshaled onto the UI thread. Of course, anyone that tracks my answers on SO knows how I feel about marshaling operations (viaInvokeor the like) as a solution for updating the UI with simple progress information. I rip on it all the time because it is generally a terrible approach.BackgroundWorkerstill forces the developer into a push model (via marshaling operations in the background), but at least it does all of this behind the scenes.The inelegance of Invoke
We all know that a UI element can only be accessed from the UI thread. This generally meant that a developer had to use marshaling operations via
ISynchronizeInvoke,DispatcherObject, orSynchronizationContextto transfer control back to the UI thread. But lets face it. These marshaling operations look ugly.Task.ContinueWithmade this a little more elegant, but the real glory goes toawaitas part of C# 5’s new asynchronous programming model.awaitcan be used to wait for aTaskto complete in such a manner that flow control is temporarily interrupted while the task is running and then returned at that very spot in the right synchronization context. There is nothing more elegant and satisfying than usingawaitas a replacement for all thoseInvokecalls.Parallel programming
I often see questions asking how things can happen in parallel. The old way was to create a few threads or use the
ThreadPool. .NET 4.0 gave use the TPL and PLINQ. TheParallelclass is a great way to get the iterations of a loop going in parallel. And PLINQ’sAsParallelis a different side of the same coin for plain old LINQ. These new TPL features greatly simplify this category of multithreaded programming..NET 4.5 introduces the TPL Data Flow library. It is intended to make elegant an otherwise complex parallel programming problem. It abstracts classes into blocks. They can be target blocks or source blocks. Data can flow from one block to another. There are many different blocks including
BufferBlock<T>,BroadcastBlock<T>,ActionBlock<T>, etc. that all do different things. And, of course, the whole library will be optimized for use with the newasyncandawaitkeywords. It is an exciting new set of classes that I think will slowly catch on.Graceful termination
How do you get a thread to stop? I see this question a lot. The easiest way is to call
Thread.Abort, but we all know the perils of doing this…I hope. There are many different ways to do this safely. .NET 4.0 introduced a more unified concept called cancellation viaCancellationTokenandCancellationTokenSource. Background tasks can pollIsCancellationRequestedor just callThrowIfCancellationRequestedat safe points to gracefully interrupt whatever work they were doing. Other threads can callCancelto request cancellation.