Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   The Concurrent Collection Classes

The FCL ships with four thread-safe collection classes, all of which are in the System.Collections. Concurrent namespace: ConcurrentQueue, ConcurrentStack, ConcurrentDictionary, and ConcurrentBag. Here is what some of their most commonly used members look like.

 

// Process items in a first­in, first­out order (FIFO)

public class ConcurrentQueue<T> : IProducerConsumerCollection<T>, IEnumerable<T>, ICollection, IEnumerable {

 

public ConcurrentQueue(); public void Enqueue(T item);

public Boolean TryDequeue(out T result); public Int32 Count { get; }

public IEnumerator<T> GetEnumerator();

}


// Process items in a last­in, first­out order (LIFO)

public class ConcurrentStack<T> : IProducerConsumerCollection<T>, IEnumerable<T>, ICollection, IEnumerable {

 

public ConcurrentStack(); public void Push(T item);

public Boolean TryPop(out T result); public Int32 Count { get; }

public IEnumerator<T> GetEnumerator();

}

 

// An unordered set of items where duplicates are allowed public class ConcurrentBag<T> : IProducerConsumerCollection<T>,

IEnumerable<T>, ICollection, IEnumerable {

 

public ConcurrentBag(); public void Add(T item);

public Boolean TryTake(out T result); public Int32 Count { get; }

public IEnumerator<T> GetEnumerator();

}

 

// An unordered set of key/value pairs

public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable {

 

public ConcurrentDictionary();

public Boolean TryAdd(TKey key, TValue value);

public Boolean TryGetValue(TKey key, out TValue value); public TValue this[TKey key] { get; set; }

public Boolean TryUpdate(TKey key, TValue newValue, TValue comparisonValue); public Boolean TryRemove(TKey key, out TValue value);

public TValue AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue> updateValueFactory);

public TValue GetOrAdd(TKey key, TValue value); public Int32 Count { get; }

public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator();

}

 

All these collection classes are non-blocking. That is, if a thread tries to extract an element when no such element exists, the thread returns immediately; the thread does not block waiting for an element to appear. This is why methods like TryDequeue, TryPop, TryTake, and TryGetValue all return true if an item was obtained and returns false, if not.

These non-blocking collections are not necessarily lock-free. The ConcurrentDictionary class uses Monitor internally, but the lock is held for a very short time while manipulating the item in the collection. ConcurrentQueue and ConcurrentStack are lock-free; these both internally use Inter­ locked methods to manipulate the collection. A single ConcurrentBag object internally consists of a mini-collection object per thread. When a thread adds an item to the bag, Interlocked methods are used to add the item to the calling thread’s mini-collection. When a thread tries to extract an element from the bag, the bag checks the calling thread’s mini-collection for the item. If the item is there, then an Interlocked method is used to extract the item. If the thread’s mini-collection doesn’t have the




item, then a Monitor is taken internally to extract an item from another thread’s mini-collection. We say that the thread is stealing the item from another thread.

You’ll notice that all the concurrent classes offer a GetEnumerator method, which is typically used with C#’s foreach statement, but can also be used with Language Integrated Query (LINQ). For the ConcurrentStack, ConcurrentQueue, and ConcurrentBag, the GetEnumerator method takes a snapshot of the collection’s contents and returns elements from this snapshot; the contents of the actual collection may change while enumerating over the snapshot. ConcurrentDictionary’s GetEnumerator method does not take a snapshot of its contents, so the contents of the dictionary

may change while enumerating over the dictionary; beware of this. Also note that the Count property returns the number of elements that are in the collection at the moment you query it. The count may immediately become incorrect if other threads are adding or removing elements from the collection at the same time.

Three of the concurrent collection classes, ConcurrentStack, ConcurrentQueue, and ConcurrentBag, implement the IProducerConsumerCollection interface, which is defined as follows.

 

public interface IProducerConsumerCollection<T> : IEnumerable<T>, ICollection, IEnumerable { Boolean TryAdd(T item);

Boolean TryTake(out T item); T[] ToArray();

void CopyTo(T[] array, Int32 index);

}

 

Any class that implements this interface can be turned into a blocking collection, where threads producing (adding) items will block if the collection is full and threads consuming (removing) items will block if the collection is empty. Of course, I’d try to avoid using blocking collections because their purpose in life is to block threads. To turn a non-blocking collection into a blocking collection, you construct a System.Collections.Concurrent.BlockingCollection class by passing in a refer- ence to a non-blocking collection to its constructor. The BlockingCollection class looks like this (some methods are not shown).

 

public class BlockingCollection<T> : IEnumerable<T>, ICollection, IEnumerable, IDisposable { public BlockingCollection(IProducerConsumerCollection<T> collection,

Int32 boundedCapacity);

 

public void Add(T item);

public Boolean TryAdd(T item, Int32 msTimeout, CancellationToken cancellationToken); public void CompleteAdding();

 

public T Take();

public Boolean TryTake(out T item, Int32 msTimeout, CancellationToken cancellationToken);

 

public Int32 BoundedCapacity { get; } public Int32 Count { get; }

public Boolean IsAddingCompleted { get; } // true if CompleteAdding is called

public Boolean IsCompleted { get; } // true if IsAddingComplete is true and Count==0 public IEnumerable<T> GetConsumingEnumerable(CancellationToken cancellationToken);


public void CopyTo(T[] array, int index); public T[] ToArray();

public void Dispose();

}

 

When you construct a BlockingCollection, the boundedCapacity parameter indicates the maximum number of items that you want in the collection. If a thread calls Add when the underlying collection has reached its capacity, the producing thread will block. If preferred, the producing thread can call TryAdd, passing a timeout (in milliseconds) and/or a CancellationToken, so that the thread blocks until the item is added, the timeout expires, or the CancellationToken is canceled (see Chapter 27 for more information about the CancellationToken class).

The BlockingCollection class implements the IDisposable interface. When you call Dispose, it calls Dispose on the underlying collection. It also disposes of two SemaphoreSlim objects that the class uses internally to block producers and consumers.

When producers will not be adding any more items into the collection, a producer should call the CompleteAdding method. This will signal the consumers that no more items will be produced. Specifically, this causes a foreach loop that is using GetConsumingEnumerable to terminate.

The following example code demonstrates how to set up a producer/consumer scenario and signal completion.

 

public static void Main() {

var bl = new BlockingCollection<Int32>(new ConcurrentQueue<Int32>());

 

// A thread pool thread will do the consuming ThreadPool.QueueUserWorkItem(ConsumeItems, bl);

 

// Add 5 items to the collection

for (Int32 item = 0; item < 5; item++) { Console.WriteLine("Producing: " + item); bl.Add(item);

}

 

// Tell the consuming thread(s) that no more items will be added to the collection bl.CompleteAdding();

 

Console.ReadLine(); // For testing purposes

}

 

private static void ConsumeItems(Object o) { var bl = (BlockingCollection<Int32>) o;

 

// Block until an item shows up, then process it foreach (var item in bl.GetConsumingEnumerable()) {

Console.WriteLine("Consuming: " + item);

}

 

// The collection is empty and no more items are going into it Console.WriteLine("All items have been consumed");

}


When I execute the preceding code, I get the following output.

 

Producing: 0

Producing: 1

Producing: 2

Producing: 3

Producing: 4

Consuming: 0

Consuming: 1

Consuming: 2

Consuming: 3

Consuming: 4

All items have been consumed

 

If you run this yourself, the Producing and Consuming lines could be interspersed, but the All items have been consumed line will always be last.

The BlockingCollection class also has static AddToAny, TryAddToAny, TakeFromAny, and TryTakeFromAny methods. All of these methods take a BlockingCollection<T>[], in addition to an item, a timeout, and a CancellationToken. The (Try)AddToAny methods cycle through all the collections in the array until they find a collection that can accept the item because it is below capac- ity. The (Try)TakeFromAny methods cycle through all the collections in the array until they find a collection to remove an item from.


 

 

Index


Symbols

.cctor methods. See type constructors

.NET Framework

checking for installation of, 8

extension methods for casting to WinRT, 655 installing, 8

migrating older code, 459 namespace documentation, 101

reflection API, 583

stability improvements with, 34 standardization of, 24

stream projections, 655

version, checking, 8

WinRT interoperability with, 654, 655

.winmd file extension, 644

.winmdobj file extension, 659

 

A

AbandonedMutexException exception, 785

Abort method, 579, 581

Thread class, 679

aborting threads, 578

accessibility, 166

accessibility modifiers, 156

accessing resources, 505

accessor methods, 228, 231

accessibility of, 248

generic, 248

for indexers, 244

inlining, 247

naming, 245

performance of, 247

AcquireReaderLock method, 577

AcquireWriterLock method, 577


 

 

Action Center. See Windows Action Center Action delegate, 279

Activator class, 594

add-in support, 596

Add methods, arguments for, 236 AddEventHandler method, 603

AddItemHelper method, 501

AddMemoryPressure method, 538

AddParticipant method, 805

AddrOfPinnedObject method, 548

AddToAny methods, 822

AddValue method, 627, 631

administrative control over applications, 62 AggregateException objects, 699

AggregateException type, 702

AIPs (automatically implemented properties), 231 AL.exe, 52, 53

command-line switches for, 87 publisher policy assemblies, creating, 86

AllBegun method, 770

AllDone method, 771

Alloc method, 546, 548 ampersands in strings, 608 anonymous function, 410, 411

body of, 413

modifiers, unnecessary, 410

anonymous method, referencing instance members, 412

anonymous methods, vs. lambda expressions, 413 anonymous types, 237

declaring properties in, 238

defining, 239

implicitly typed arrays and, 377 AnotherHybridLock method, 793


 

 


Date: 2016-03-03; view: 827


<== previous page | next page ==>
Nbsp;   Asynchronous Synchronization | API for .NET Framework
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.012 sec.)