1 为何运用并发鸠合?
缘由重要有以下几点:
System.Collections和System.Collections.Generic称号空间中所供应的典范列表、鸠合和数组都不是线程平安的,若无同步机制,他们不适合于接收并发的指令来增添和删除元素。
在并发代码中运用上述典范鸠合须要庞杂的同步治理,运用起来很不轻易。
运用庞杂的同步机制会大大下降机能。
NET Framework 4所供应的新的鸠合只管地削减须要运用锁的次数。这些新的鸠合经由历程运用比较并交流(compare-and-swap,CAS)指令和内存屏蔽,防止运用互斥的重量级锁。这对机能有保证。
注重:与典范鸠合比拟,并发鸠合会有更大的开支,因此在串行代码中运用并发鸠合无意义,只会增添分外的开支且运转速率比接见典范鸠合慢。
2 并发鸠合
1)ConcurrentQueue:线程平安的先进先出 (FIFO) 鸠合
重要要领:
Enqueue(T item);将对象增添到鸠合末端。
TryDequeue(out T result); 尝试移除并返回位于鸠合最先处的对象,返回值示意操纵是不是胜利。
TryPeek(out T result);尝试返回鸠合最先处的对象,但不将其移除,返回值示意操纵是不是胜利。
申明:
ConcurrentQueue是完整无锁的,但当CAS操纵失利且面对资本争用时,它能够会自旋而且重试操纵。
ConcurrentQueue是FIFO鸠合,某些和相差递次无关的场所,只管不要用ConcurrentQueue。
2)ConcurrentStack:线程平安的后进先出 (LIFO) 鸠合
重要要领及属性:
Push(T item);将对象插进去鸠合的顶部。
TryPop(out T result);尝试弹出并返回鸠合顶部的对象,返回值示意操纵是不是胜利。
TryPeek(out T result);尝试返回鸠合最先处的对象,但不将其移除,返回值示意操纵是不是胜利。
IsEmpty { get; }指导鸠合是不是为空。
PushRange(T[] items);将多个对象插进去鸠合的顶部。
TryPopRange(T[] items);弹出顶部多个元素,返回效果为弹出元素个数。
申明:
与ConcurrentQueue类似地,ConcurrentStack完整无锁的,但当CAS操纵失利且面对资本争用时,它能够会自旋而且重试操纵。
猎取鸠合是不是包含元素运用IsEmpty属性,而不是经由历程推断Count属性是不是大于零。挪用Count比挪用IsEmpty开支大。
运用PushRange(T[] items)和TryPopRange(T[] items)时注重缓冲引发的分外开支和分外的内存斲丧。
3) ConcurrentBag:元素可反复的无序鸠合
重要要领及属性:
TryPeek(out T result);尝试从鸠合返回一个对象,但不移除该对象,返回值示意是不是胜利取得该对象。
TryTake(out T result);尝试从鸠合返回一个对象并移除该对象,返回值示意是不是胜利取得该对象。
Add(T item);将对象增添到鸠合中。
IsEmpty { get; }诠释同ConcurrentStack
申明:
ConcurrentBag为每一个接见鸠合的线程保护了一个当地行列,在能够的情况下,它会以无锁的体式格局接见当地行列。
ConcurrentBag在同一个线程增添和删除元素的场所下效力异常高。
由于ConcurrentBag有时会须要锁,在生产者线程和消耗者线程完整离开的场景下效力异常低。
ConcurrentBag挪用IsEmpty的开支异常大,由于这须要暂时取得这个无序组的一切锁。
4)BlockingCollection:完成
System.Collections.Concurrent.IProducerConsumerCollection<T> 的线程平安鸠合,供应壅塞和限定功用
重要要领及属性:
BlockingCollection(int boundedCapacity);boundedCapacity示意鸠合限定大小。
CompleteAdding();将BlockingCollection实例标记为不再接收任何增添。
IsCompleted { get; }此鸠合是不是已标记为已完成增添而且为空。
GetConsumingEnumerable();从鸠合中移除并返回移除的元素
Add(T item);增添元素到鸠合。
TryTake(T item, int millisecondsTimeout, CancellationToken cancellationToken);
申明:
运用BlockingCollection()组织函数实例化BlockingCollection,意味着不设置boundedCapacity,那末boundedCapacity为默许值: int.MaxValue。
限界:运用BlockingCollection(int boundedCapacity),设置boundedCapacity的值,当鸠合容量到达这个值得时刻,向BlockingCollection增添元素的线程将会被壅塞,直到有元素被删除。
限界功用可掌握内存中鸠合最大大小,这关于须要处置惩罚大批元素的时刻异常有效。
默许情况下,BlockingCollection封装了一个ConcurrentQueue。能够在组织函数中指定一个完成了IProducerConsumerCollection接口的并发鸠合,包含:ConcurrentStack、ConcurrentBag。
运用此鸠合包含易于无限定守候的风险,所以运用TryTake越发,由于TryTake供应了超时掌握,指定的时候内能够从鸠合中移除某个项,则为 true;否则为 false。
5)ConcurrentDictionary:可由多个线程同时接见的键值对的线程平安鸠合。
重要要领
AddOrUpdate(TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory);假如指定的键尚不存在,则将键/值对增添到 字典中;假如指定的键已存在,则更新字典中的键/值对。
GetOrAdd(TKey key, TValue value);假如指定的键尚不存在,则将键/值对增添到字典中。
TryRemove(TKey key, out TValue value);尝试从字典中移除并返回具有指定键的值。
TryUpdate(TKey key, TValue newValue, TValue comparisonValue);将指定键的现有值与指定值举行比较,假如相称,则用第三个值更新该键。
申明:
ConcurrentDictionary关于读操纵是完整无锁的。当多个使命或线程向个中增添元素或修正数据的时刻,ConcurrentDictionary运用细粒度的锁。运用细粒度的锁只会锁定真正须要锁定的部份,而不是全部字典。
6)IProducerConsumerCollection:定义供生产者/消耗者用来操纵线程平安鸠合的要领。 此接口供应一个一致的示意(为生产者/消耗者鸠合),从而更高级别笼统如 System.Collections.Concurrent.BlockingCollection<T>能够运用鸠合作为基础的存储机制。
3.经常使用情势
1)并行的生产者-消耗者情势
定义:
生成者和消耗者是此情势中的两类对象模子,消耗者依赖于生产者的效果,生产者生成效果的同时,消耗者运用效果。
图1 并行的生产者-消耗者情势
申明:
并发鸠合用在此情势下异常适宜,由于并发鸠合支撑此情势中对象的并行操纵。
若不运用并发鸠合,那末就要到场同步机制,从而使顺序变得比较庞杂,难于保护和明白,同时大大下降机能。
上图为生产者消耗者情势示意图,纵轴为时候轴,生成者与消耗者的并不在一条时候线上,但两者有交织,意在表明生成者先发生效果,然后消耗者才真正运用了生成者发生的数据。
2)流水线情势
定义:
流水线由多个阶段组成,每一个阶段由一系列的生产者和消耗者组成。平常来说前一个阶段是后一个阶段的生成者;依托相邻两个阶段之间的缓冲区行列,每一个阶段能够并发实行。
图2 并行的流水线情势
申明:
常运用BlockingCollection<T>作为缓冲罐区行列。
流水线的速率近似即是流水线最慢阶段的速率。
上图为流水线情势示意图,前一阶段为后一阶段的生成者,这里展现了最为简朴和基础的流水线情势,更庞杂的情势能够认为是每一个阶段都包含了对数据更多的处置惩罚历程。
4 运用体式格局
仅以ConcurrentBag和BlockingCollection为例,其他的并发鸠合与之类似。
ConcurrentBag
List<string> list = ...... ConcurrentBag<string> bags = new ConcurrentBag<string>(); Parallel.ForEach(list, (item) => { //对list中的每一个元素举行处置惩罚然后,到场bags中 bags.Add(itemAfter); });
BlockingCollection—生产者消耗者情势
public static void Execute() { //挪用Invoke,使得生产者使命和消耗者使命并行实行 //Producer要领和Customer要领在Invoke中的参数递次恣意,不管何种递次都邑取得准确的效果 Parallel.Invoke(()=>Customer(),()=>Producer()); Console.WriteLine(string.Join(",",customerColl)); } //生产者鸠合 private static BlockingCollection<int> producerColl = new BlockingCollection<int>(); //消耗者鸠合 private static BlockingCollection<string> customerColl = new BlockingCollection<string>(); public static void Producer() { //轮回将数据到场生成者鸠合 for (int i = 0; i < 100; i++) { producerColl.Add(i); } //设置信号,表明不在向生产者鸠合中到场新数据 //能够设置越发庞杂的关照情势,比方数据量到达肯定值且个中的数据满足某一前提时就设置完成增添 producerColl.CompleteAdding(); } public static void Customer() { //挪用IsCompleted要领,推断生产者鸠合是不是在增添数据,是不是另有未"消耗"的数据 //注重不要运用IsAddingCompleted,IsAddingCompleted只表明鸠合标记为已完成增添,而不能申明其为空 //而IsCompleted为ture时,那末IsAddingCompleted为ture且鸠合为空 while (!producerColl.IsCompleted) { //挪用Take或TryTake "消耗"数据,消耗一个,移除一个 //TryAdd的优点是供应超时机制 customerColl.Add(string.Format("消耗:{0}", producerColl.Take())); } }
以上就是.NET多线程编程—并发鸠合的内容,更多相关内容请关注ki4网(www.ki4.cn)!