What .NET 4.5 (or earlier) higher-level constructs make Threading easier?

前端 未结 4 1549
借酒劲吻你
借酒劲吻你 2021-02-19 23:33

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 (o

4条回答
  •  悲&欢浪女
    2021-02-20 00:03

    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 foreach loop by creating a new variable for each iteration. You will no longer have to create a special variable for lambda expression closures. Unfortunately, the for loop will still need to be handle with a special capturing variable.

    Waiting for asynchronous tasks to complete

    .NET 4.0 introduced the CountdownEvent class which encapsulates a lot of the logic required to wait for the completion of many tasks. Most junior developers used Thread.Join calls or a single WaitHandle.WaitAll call. Both of these have scalability problems. The old pattern was to use a single ManualResetEvent and signal it when a counter reached zero. The counter was updated using the Interlocked class. CountdownEvent makes 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 Task class which can have child tasks chained off of it via TaskCreationOptions.AttachedToParent. If you call Task.Wait on a parent it will wait for all child tasks to complete as well.

    Producer-Consumer

    .NET 4.0 introduced the BlockingCollection class which acts like a normal queue except that it can block when the collection is empty. You can queue an object by calling Add and dequeue an object by calling Take. Take blocks 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 BackgroundWorker made spinning off a background task from a WinForm application a lot easier for novice developers. The main benefit is that you can call ReportProgress from within the DoWork event handler and the ProgressChanged event 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 (via Invoke or 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. BackgroundWorker still 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, or SynchronizationContext to transfer control back to the UI thread. But lets face it. These marshaling operations look ugly. Task.ContinueWith made this a little more elegant, but the real glory goes to await as part of C# 5's new asynchronous programming model. await can be used to wait for a Task to 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 using await as a replacement for all those Invoke calls.

    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. The Parallel class is a great way to get the iterations of a loop going in parallel. And PLINQ's AsParallel is 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, BroadcastBlock, ActionBlock, etc. that all do different things. And, of course, the whole library will be optimized for use with the new async and await keywords. 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 via CancellationToken and CancellationTokenSource. Background tasks can poll IsCancellationRequested or just call ThrowIfCancellationRequested at safe points to gracefully interrupt whatever work they were doing. Other threads can call Cancel to request cancellation.

提交回复
热议问题