InfoQ

News

Coordination Data Structures: New Classes for .NET Multithreading

Posted by Jonathan Allen on Jun 05, 2008 06:28 AM

Community
.NET
Topics
Performance & Scalability
Tags
Parallel Programming

The June drop of Parallel Extensions for .NET added a set of classes to make sharing data in a multi-threaded application easier. With ten new classes including new synchronization primitives, futures, and new collection classes, there is only time to touch on each of them briefly.

This first batch of classes is in the System.Threading namespace.

CountdownEvent allows for coordination between an unlimited number of threads. The counter is either preset or incremented as each thread is started. As the threads complete their task they decrement the counter. A call to CountdownEvent.Wait blocks the main thread until the counter to reaches zero. In this sense the CountdownEvent is similar to AutoResetEvent and ManualResetEvent.

LazyInit is essentially what many people refer to as a Future. A LazyInit object takes a class or delegate. If it gets a class it will create a new instance with the default constructor when the Value property is called. If given a delegate the result of the delegate is stored and returned.

LazyInit has three modes for value creation. AllowMultipleExecution allows each thread to attempt to be the first to initialize the value, but ensures only one object is ever returned. This could result in multiple objects being created and discarded. EnsureSingleExecution guarantees that only one instance is ever created. Finally, ThreadLocal gives a different instance to each thread.

WriteOnce can be seen as an alternative to LazyInit. Like LazyInit, it's value can only be set once and becomes immutable thereafter. However, the value for WriteOnce is assigned externally to the class. Once set, it can never change.

WriteOnce is especially useful when you want read only semantics but cannot use the readonly modifier because you do not want to assign the value in the constructor. If you try to assign a value to a WriteOnce object more than once, it will be marked as "corrupted" and can no longer be read. Using TrySetValue instead of the Value property prevents this corruption from occurring.

ManualResetEventSlim is a lightweight version of ManualResetEvent. Unlike the older version, it does not rely on kernel objects and is not finalizable. This should result in better performance, especially when they need to be created frequently.

SemaphoreSlim, like ManualResetEventSlim, replaces the thin wrapper around the kernel with a lightweight alternative.

Two more lightweight objects, SpinLock and SpinWait, are really only suitable for multi-core and multi-processor machines. Both leave the blocked thread active, essentially wasting CPU cycles. They are useful when the expected wait time is very short and context switches have become a bottleneck.

The second group of classes is in the System.Threading.Collections namespace.

ConcurrentQueue is a queue structure designed with multithreading in mind. Older queues, even when "thread-safe" required locks so that you can check the Count property and call Dequeue as an atomic action. ConcurrentQueue avoids that pitfall by only offering a TryDequeue method. Since it is safe to call without checking the count, no explicit locks are needed.

ConcurrentStack works the same way, though obviously with stack semantics.

BlockingCollection, designed for multiple readers and writers, is a rather complex thing with many features one normally implements separately. First of all, BlockingCollection can be used on its own as a collection or the semantics can change by having it wrap an IConcurrentCollection object such as ConcurrentStack and ConcurrentQueue.

Unlike most collections, BlockingCollection supports something a method called GetConsumingEnumerable. This allows one to use a for-each loop or LINQ query against the collection in a thread safe manner. Normally destructive operations like consuming items from a queue would trigger an exception when using either construct.

In order to throttle your writers, BlockingCollections can be given an upper size limit. When this limit is exceeded, calls that add to the collection are blocked.

BlockingCollections also have the concept of being "complete". When you call CompleteAdding, consumers are notified that no new items will be added to the collection and that they can stop processing after the current batch.

Finally, BlockingCollections can also be used in groups. If you pass an object to AddAny along with an array of BlockingCollections, the object will be added to one of them. The documentation is still sparse, but presumably the collection selected by which is smallest. This call is blocking if all the collections are full.

 

No comments

Watch Thread Reply

Educational Content

Bindings, Platforms, and Innovation

This presentation focuses on the Internet and separating myth from fact, history from the future, and the mundane from the imaginative. Bob Frankston presents a vision of what could and should be.

Orchestrating Long Running Activities with JBoss / JBPM

This article explores the use of JBoss and jBPM to implement design solutions that effectively address the issue of orchestrating long running activities.

Neo4j - The Benefits of Graph Databases

This presentation covers the use of graph databases as an optimal solution for data that is difficult to fit in static tables, rapidly evolving data or data that has a lot of optional attributes.

Realistic about Risk: Software development with Real Options

This session introduces Real Options and shows how it can help in running your project. Real Options is a decision-making process that can be used to manage risk.

Communication Flexibility Using Bindings

This article discusses the use of bindings on services and references (including the instance of non-configured bindings) as the means to implement SCA communications in a Web and SOA environment.

Writing DSLs in Groovy

After a short introduction to DSLs, Scott Davis plays with the keyboard showing how to approach the creation of a DSL by typing working snippets of Groovy code that get executed.

Scaling Agile with C/ALM (Collaborative Application Lifecycle Management)

IBM Rational and InfoQ present, Scaling Agile with C/ALM, an eBook showing organizations how to become “finely tuned software delivery machines” by enabling team integration and scaling.

Concurrent Programming with Microsoft F#

Amanda Laucher presents a real life enterprise application written in F#. She shows actual code snippets, explaining design decisions and suggesting how to use some of the F# constructs.