这一章主要介绍了System.Collections.Concurrent下的几个类。

ConcurrentQueue<T>

并发队列。完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。

//加入队尾
public  void Enqueue(T item)
//尝试删除队头,并将元素通过out返回,返回值表示是否操作成功
public  bool TryDequeue( out T result)
//尝试获取队头,通过out返回元素,返回值为代表是否操作成功
public  bool TryPeek( out

ConcurrentStack<T>

并发栈,完全无锁,使用CAS(compare-and-swap)比较并交换和自旋重试来实现线程安全。

 

public  void Push(T item)
public  void PushRange(T[] items)
public  void PushRange(T[] items, int startIndex, int count)
 
public  bool TryPeek( out T result)
 
public  bool TryPop( out T result)
public  int TryPopRange(T[] items)
public  int TryPopRange(T[] items, int startIndex, int

ConcurrentBag<T>

这是一个无序的对象集合,而且支持对象重复。在同一线程下添加和删除效率高,又是需要锁。

public  void Add(T item)
public  bool TryPeek( out T result)
public  bool TryTake( out

ConcurrentDictionary<TKey,TValue>

并发的键值对,读是没有锁的,写会有细粒度的锁。

 

public TValue GetOrAdd(TKey key,TValue value)
public  bool TryAdd(TKey key,TValue value)
public  bool TryGetValue(TKey key, out TValue value)
public  bool TryRemove(TKey key, out TValue value)
public  bool

IProducerConsumer<T>与BlockingCollection<T>

IProducerConsumerCollection<T>是对生产者-消费者模型的一个操作抽象,BlockingCollection<T>是一个实现。

针对生产者消费者模型,给出了很多方便的功能。

构造的时候可以设置最大容量,当集合达到最大容量,添加请求会被阻塞。

添加完成的判断也不需要自己在维护标识符

更有GetConsumingEnumerable(),获取集合迭代器,可以使用foreach,如果集合为空着会阻塞,并等待添加新的元素,如果集合没有元素并且IsAddingCompleted为true那么循环终止,可以省去不必要的自旋。

还支持超时和取消功能

 

private  const  int NUM_SENTENCES  =  2000000;
private  static BlockingCollection < string > _sentencesBC;
private  static BlockingCollection < string > _capWordsInSentencesBC;
private  static BlockingCollection < string > _finalSentencesBC;
 
private  static  void ProduceSentences(System.Threading.CancellationToken ct)
{
     //...
     if ( !_sentencesBC.TryAdd(newSentence,  2000, ct))
    {
         throw  new TimeoutException(...);
    }
     //...
    _sentencesBC.CompleteAdding();
}
 
private  static  void CapitalizeWordsInSentences()
{
     //...
     while ( !_sentencesBC.IsCompleted)
    {
         string sentence;
         if (_sentencesBC.TryTake( out sentence))
        {
            _capWordsInSentencesBC.Add(...);
        }
    }
    _capWordsInSentencesBC.CompleteAdding();
}
 
private  static  void RemoveLettersInSentences()
{
     //...
     foreach (var sentence  in _capWordsInSentencesBC.GetConsumingEnumerable())
    {
        _finalSentencesBC.Add(RemoveLetters(letterChars, sentence));
    }
    _finalSentencesBC.CompleteAdding();
}
 
static  void Main( string[] args)
{
    _sentencesBC  =  new BlockingCollection < string >(NUM_SENTENCES);
    _capWordsInSentencesBC  =  new BlockingCollection < string >(NUM_SENTENCES);
    _finalSentencesBC  =  new BlockingCollection < string >(NUM_SENTENCES);
 
    var cts  =  new System.Threading.CancellationTokenSource();
    var ct  = cts.Token;
    var deferredCancelTask  = Task.Factory.StartNew(()  = >
    {
        System.Threading.Thread.Sleep( 500);
        cts.Cancel();
    });
 
    Parallel.Invoke(
        ()  = > ProduceSentences(ct),
        ()  = > CapitalizeWordsInSentences(),
        ()  = > RemoveLettersInSentences()
        );
}


任务计数器

常常需要知道还在运行的Task的数量。所以需要对计数器进行原子的加减

可以在任务新建的时候使用System.Threading.Interlocked.Increment(ref tasksRunning)

在任务结束后System.Threading.Interlocked.Decrement(ref tasksRunning);

private  static  int tasksRunning  =  0;
 
//inti method
for ( int i  =  0; i  < taskMax; i ++)
{
    System.Threading.Interlocked.Increment( ref tasksRunning);
    tasks[i]  = Task.Factory.StartNew(()  = >
    {
         try
        {
             //...
        }
         finally
        {
            System.Threading.Interlocked.Decrement( ref tasksRunning);
        }
    });
}
 
//other method
while ((tasksRunning  >  0))
{
     //...
}

并发集合和不安全集合互转

并发集合可以结构一个IEnumerable接口的集合做构造函数参数。

例如


string[] _invalidHexValues  = {  "AF",  "BD",  "BF",  "CF",  "DA",  "FA",  "FE",  "FF" };
var invalidHexValuesStack  =  new ConcurrentStack < string >(_invalidHexValues);

要把并发集合转成不安全的可以使用CopyTo ToArray等方法

volatile关键字

使用volatile可以确保在不同的线程中进行访问的时候,可以得到最新值。这些变量不会被编译器按照只在一个线程中的进行访问的假定进行优化。

private  static  volatile  bool _flag  =  false;