Parallel Programming : Concurrent Collections

10/10/2010 3:51:08 PM
Parallel computing basically relies on multithreading, although with some particular specifications for taking advantage of multicore architectures. The real problem is when you need to work with collections, because in a multithreaded environment, multiple threads could access a collection attempting to make edits that need to be controlled. The .NET Framework 4.0 introduces a number of thread-safe concurrent collections, exposed by the System.Collections.Concurrent namespace, which is useful in parallel computing with the .NET Framework because they grant concurrent access to their members from threads.

What does Thread-Safe Mean?

A collection is thread-safe when access to its members is allowed to only one thread per time (or to a few threads in particular cases).

Table 1 summarizes concurrent collections in .NET 4.0.

Table 1. Available Concurrent Collections
ConcurrentBag(Of T)Represents an unordered collection of items
ConcurrentQueue(Of T)Represents a concurrent FIFO collection
ConcurrentStack(Of T)Represents a concurrent LIFO collection
ConcurrentDictionary(Of TKey, TValue)Represents a concurrent Dictionary(Of TKey, TValue)
BlockingCollection(Of T)A thread-safe collection with bounding and blocking capabilities against threads

ConcurrentBag(Of T)

The ConcurrentBag(Of T) is the most basic concurrent collection, in that it is just an unordered collection of items. The following code demonstrates how you use it for adding, iterating, counting, and removing items:

'Creating an instance
Dim cb As New ConcurrentBag(Of String)

'Adding some items
cb.Add("String one")
cb.Add("String two")
cb.Add("String three")

'Showing items count

'Listing items in the collection
For Each item In cb

'Removing an item
Dim anItem As String = String.Empty

You add items to the collection by invoking the Add method. The Count property gets the number of items in the collection, whereas the IsEmpty property tells you if the collection is empty. To remove an item, you invoke TryTake, which takes the first item, assigns it to the result variable (in this case anItem), and then removes it from the collection. It returns True if removing succeeds, otherwise False. Keep in mind that this collection offers no order for items; therefore, iteration results are completely random.

ConcurrentQueue(Of T)

The ConcurrentQueue(Of T) collection is just a thread-safe implementation of the Queue(Of T) collection; therefore, it takes the logic of FIFO (First-In, First Out), where the first element in the collection is the first to be removed. The following code shows an example:

'Creating an instance
Dim cq As New ConcurrentQueue(Of Integer)

'Adding items

'Removing an item from the queue
Dim item As Integer

'Returns "1":

The main difference with Queue is how items are removed from the queue. In this concurrent implementation, you invoke TryDequeue, which passes the removed item to a result variable by reference. The method returns True in cases of success, otherwise False. Still the Count property returns the number of items in the queue.

ConcurrentStack(Of T)

ConcurrentStack(Of T) is the thread-safe implementation of the Stack(Of T) generic collection and works according to the LIFO (Last-In, First-Out) logic. The following code shows an example of using this collection:

'Creating an instance
Dim cs As New ConcurrentStack(Of Integer)

'Adding an item
'Adding an array
cs.PushRange(New Integer() {10, 5, 10, 20})

Dim items() As Integer = New Integer(3) {}

'Removing an array
cs.TryPopRange(items, 0, 4)

'Iterating the array
Array.ForEach(Of Integer)(items, Sub(i)
End Sub)
'Removing an item
Dim anItem As Integer

The big difference between this collection and its thread-unsafe counterpart is that you can also add an array of items invoking PushRange, whereas you still invoke Push to add a single item. To remove an array from the stack, you invoke TryPopRange, which takes three arguments: the target array that will store the removed items, the start index, and the number of items to remove. Both PushRange and TryPopRange return a Boolean value indicating if they succeeded. The Array.ForEach loop in the preceding code is just an example for demonstrating how the array was actually removed from the collection. Finally, you invoke TryPop for removing an item from the stack; such an item is then assigned to a result variable, passed by reference.

ConcurrentDictionary(Of TKey, TValue)

The ConcurrentDictionary collection has the same purpose of its thread-unsafe counterpart, but it differs in how methods work. All methods for adding, retrieving, and removing items return a Boolean value indicating success or failure, and their names all start with Try. The following code shows an example:

'Where String is for names and Integer for ages
Dim cd As New ConcurrentDictionary(Of String, Integer)

Dim result As Boolean

'Adding some items
result = cd.TryAdd("Alessandro", 32)
result = cd.TryAdd("Nadia", 28)
result = cd.TryAdd("Roberto", 35)

'Removing an item
result = cd.TryRemove("Nadia", 28)

'Getting a value for the specified key
Dim value As Integer
result = cd.TryGetValue("Alessandro", value)


The logic of the collection is then the same of Dictionary, so refer to this one for details.

BlockingCollection(Of T)

The BlockingCollection(Of T) is a special concurrent collection. At the highest level the collection has two characteristics. The first is that if a thread attempts to retrieve items from the collection while it is empty, the thread is blocked until some items are added to the collection; the second one is that if a thread attempts to add items to the collection, but this has reached the maximum number of items possible, the thread is blocked until some space is freed in the collection. Another interesting feature is completion; you can mark the collection as complete so that no other items can be added. This is accomplished via the CompleteAdding instance method. After you invoke this method, if a thread attempts to add items, an InvalidOperationException is thrown. The following code shows how to create a BlockingCollection for strings:

Dim bc As New BlockingCollection(Of String)


'Marks the collection as complete

'Returns an exception

'Removes an item from the collection (FIFO)
Dim result = bc.Take()

You add items by invoking the Add method, and you mark the collection complete with CompleteAdding. To remove an item, you invoke Take. This method removes the first item added to the collection, according to the FIFO approach. This is because the BlockingCollection is not actually a storage collection, whereas it creates a ConcurrentQueue behind the scenes, adding blocking logic to this one. The class also exposes some properties:

  • BoundedCapacity, which returns the bounded capacity for the collection. You can provide the capacity via the constructor. If not, the property returns -1 as the value indicating that it’s a growing collection.

  • IsCompleted, which indicates if the collection has been marked with CompleteAdding and it is also empty.

  • IsAddingCompleted, which indicates if the collection has been marked with CompleteAdding.

The class has other interesting characteristics. For example, the beginning of the discussion explained why it is considered as “blocking.” By the way, it also offers methods whose names all begin with Try, such as TryAdd and TryTake, which provides overloads that enable doing their respective work without being blocked. The last feature of the BlockingCollection is a number of static methods that you can use for adding and removing items to and from multiple BlockingCollection instances simultaneously, both blocking and nonblocking. Such methods are AddToAny, TakeFromAny, TryAddToAny, and TryTakeFromAny. The following code shows an example of adding a string to multiple instances of the collection:

Dim collection1 As New BlockingCollection(Of String)
Dim collection2 As New BlockingCollection(Of String)

Dim colls(1) As BlockingCollection(Of String)
colls(0) = collection1
colls(1) = collection2

BlockingCollection(Of String).AddToAny(colls, "anItem")

All mentioned methods take an array of collections; this is the reason for the code implementation as previously illustrated.

Top 10
Personalizing Windows 7 (part 5) - Choosing Your Mouse Pointers
UML Essentials - UML at a Glance
Windows 7 : Understanding User Account Control and Its Impact on Performance
Adobe InDesign CS5 : Importing Graphic Objects (part 2) - Importing Graphics with the Place Command
Personalizing Windows 7 (part 2) - Choosing Your Desktop Background
Programming Excel with VBA and .NET : Variables (part 4) - User-Defined Types & Objects
Architecting a SharePoint 2010 Deployment : Choosing the Right Hardware for SharePoint
Windows Live OneCare
Exploring Group Policy in Windows 7
Anatomy of Utrabooks (Part 2) - Acer Aspire S3
Most View
Windows Azure : Using the Blob Storage API
Windows Mobile Security - Permissions and User Controls
Programmatic Security (part 6) - Assembly-Wide Permissions
Building and Deploying Applications for Windows Azure : Creating a Demo Project
Windows Phone 7 Advanced Programming Model : Advanced Data Binding (part 1)
a-Jays Four
Sharepoint 2010 : Monitoring SQL Server in a SharePoint Environment
SQL Server 2008 : Performance Tuning - Using Dynamic Management Views
Silverlight Recipes : Managing XAML Resources
Scheduling Maintenance Tasks in Vista
Windows 7 : Configuring Remote Management
Programming Hashing Algorithms (part 2) - Instantiating the Algorithm
Programming with DirectX : Additional Texture Mapping - Image Filters
Building Your First Windows Phone 7 Application (part 1) - Creating a Windows Phone Project
Managing the Cache
Building Android Apps: Create an Android Virtual Device
SQL Server 2008 : Transact-SQL Programming - The TABLESAMPLE Clause
Transact-SQL in SQL Server 2008 : New date and time Data Types and Functions
iPhone Programming : Table-View-Based Applications - Building a Model
Using Text in XAML