转载

对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问。经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作。 .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度。

基本信息

.NetFramework中并行集合的名字空间: System.Collections.Concurrent

并行容器:

•ConcurrentQueue
•ConcurrentStack
•ConcurrentBag : 一个无序的数据结构集,当不需要考虑顺序时非常有用。
•BlockingCollection : 与经典的阻塞队列数据结构类似
•ConcurrentDictionary 这些集合在某种程度上使用了无锁技术(CAS Compare-and-Swap和内存屏障 Memory Barrier),与加互斥锁相比获得了性能的提升。但在串行程序中,最好不用这些集合,它们必然会影响性能。 关于CAS: •http://www.tuicool.com/articles/zuui6z
•http://www.360doc.com/content/11/0914/16/7656248_148221200.shtml 关于内存屏障 •http://en.wikipedia.org/wiki/Memory_barrier 用法与示例 ConcurrentQueue 其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。 •Enqueue:在队尾插入元素
•TryDequeue:尝试删除队头元素,并通过out参数返回
•TryPeek:尝试将对头元素通过out参数返回,但不删除该元素。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_1_concurrent_queue
{
class Program
{
internal static ConcurrentQueue<int> _TestQueue; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = ; i < ; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestQueue.Enqueue(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = ;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeuue = _TestQueue.TryDequeue(out i);
if (IsDequeuue)
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " ====="); if (i == )
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestQueue = new ConcurrentQueue<int>(); Console.WriteLine("Sample 3-1 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 3-1 Main }"); Console.ReadKey();
}
}
} ConcurrentStack 其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。 •Push:向栈顶插入元素
•TryPop:从栈顶弹出元素,并且通过out 参数返回
•TryPeek:返回栈顶元素,但不弹出。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_2_concurrent_stack
{
class Program
{
internal static ConcurrentStack<int> _TestStack; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = ; i < ; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestStack.Push(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = ;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeuue = _TestStack.TryPop(out i);
if (IsDequeuue)
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i); if (i == )
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestStack = new ConcurrentStack<int>(); Console.WriteLine("Sample 4-1 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-1 Main }"); Console.ReadKey();
}
}
} 测试中一个有趣的现象: 虽然生产者已经在栈中插入值已经到了25,但消费者第一个出栈的居然是4,而不是25。很像是出错了。但仔细想想入栈,出栈和打印语句是两个部分,而且并不是原子操作,出现这种现象应该也算正常。 Sample - Main {
Main t1 t2 started {
Main t1 t2 started }
Main wait t1 t2 end {
ThreadWork1 run {
ThreadWork1 producer:
ThreadWork2 run {
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork2 consumer: =====
ThreadWork2 consumer: =====
ThreadWork2 consumer: =====
ThreadWork2 consumer: =====
ThreadWork1 producer:
ThreadWork1 producer:
ThreadWork1 producer: ConcurrentBag 一个无序的集合,程序可以向其中插入元素,或删除元素。 在同一个线程中向集合插入,删除元素的效率很高。 • Add:向集合中插入元素
• TryTake:从集合中取出元素并删除
• TryPeek:从集合中取出元素,但不删除该元素。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_3_concurrent_bag
{
class Program
{
internal static ConcurrentBag<int> _TestBag; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = ; i < ; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestBag.Add(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = ;
int nCnt = ;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (;;)
{
IsDequeuue = _TestBag.TryTake(out i);
if (IsDequeuue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nCnt++;
} if (nCnt == )
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestBag = new ConcurrentBag<int>(); Console.WriteLine("Sample 4-3 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-3 Main }"); Console.ReadKey();
}
}
} BlockingCollection 一个支持界限和阻塞的容器 •Add :向容器中插入元素
•TryTake:从容器中取出元素并删除
•TryPeek:从容器中取出元素,但不删除。
•CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。
•IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。 示例程序: 程序中,消费者线程完全使用 while (!_TestBCollection.IsCompleted) 作为退出运行的判断条件。 在Worker1中,有两条语句被注释掉了,当i 为50时设置CompleteAdding,但当继续向其中插入元素时,系统抛出异常,提示无法再继续插入。 using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_4_concurrent_bag
{
class Program
{
internal static BlockingCollection<int> _TestBCollection; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = ; i < ; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestBCollection.Add(i);
//if (i == 50)
// _TestBCollection.CompleteAdding();
}
_TestBCollection.CompleteAdding(); System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = ;
int nCnt = ;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
while (!_TestBCollection.IsCompleted)
{
IsDequeuue = _TestBCollection.TryTake(out i);
if (IsDequeuue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nCnt++;
}
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2()); _TestBCollection = new BlockingCollection<int>(); Console.WriteLine("Sample 4-4 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 4-4 Main }"); Console.ReadKey();
}
}
} 当然可以尝试在Work1中注释掉 CompleteAdding 语句,此时Work2陷入循环无法退出。 ConcurrentDictionary 对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁。 •AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。
•GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。
•TryAdd:尝试在容器中添加新的键和值。
•TryGetValue:尝试根据指定的键获得值。
•TryRemove:尝试删除指定的键。
•TryUpdate:有条件的更新当前键所对应的值。
•GetEnumerator:返回一个能够遍历整个容器的枚举器。 程序示例: using System;
using System.Text; using System.Threading.Tasks;
using System.Collections.Concurrent; namespace Sample4_5_concurrent_dictionary
{
class Program
{
internal static ConcurrentDictionary<int, int> _TestDictionary; class ThreadWork1 // producer
{
public ThreadWork1()
{ } public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = ; i < ; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestDictionary.TryAdd(i, i);
} System.Console.WriteLine("ThreadWork1 run } ");
}
} class ThreadWork2 // consumer
{
public ThreadWork2()
{ } public void run()
{
int i = , nCnt = ;
int nValue = ;
bool IsOk = false;
System.Console.WriteLine("ThreadWork2 run { ");
while (nCnt < )
{
IsOk = _TestDictionary.TryGetValue(i, out nValue);
if (IsOk)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
nValue = nValue * nValue;
_TestDictionary.AddOrUpdate(i, nValue, (key, value) => { return value = nValue; });
nCnt++;
i++;
}
}
System.Console.WriteLine("ThreadWork2 run } ");
}
} static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
} static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2());
bool bIsNext = true;
int nValue = ; _TestDictionary = new ConcurrentDictionary<int, int>(); Console.WriteLine("Sample 4-5 Main {"); Console.WriteLine("Main t1 t2 started {");
t1.Start();
t2.Start();
Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {");
Task.WaitAll(t1, t2);
Console.WriteLine("Main wait t1 t2 end }"); foreach (var pair in _TestDictionary)
{
Console.WriteLine(pair.Key + " : " + pair.Value);
} System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<int, int>>
enumer = _TestDictionary.GetEnumerator(); while (bIsNext)
{
bIsNext = enumer.MoveNext();
Console.WriteLine("Key: " + enumer.Current.Key +
" Value: " + enumer.Current.Value); _TestDictionary.TryRemove(enumer.Current.Key, out nValue);
} Console.WriteLine("\n\nDictionary Count: " + _TestDictionary.Count); Console.WriteLine("Sample 4-5 Main }"); Console.ReadKey();
}
}
}

C# 线程安全集合的更多相关文章

  1. C#的变迁史 - C# 4.0 之线程安全集合篇

    作为多线程和并行计算不得不考虑的问题就是临界资源的访问问题,解决临界资源的访问通常是加锁或者是使用信号量,这个大家应该很熟悉了. 而集合作为一种重要的临界资源,通用性更广,为了让大家更安全的使用它们, ...

  2. 线程安全集合 ConcurrentDictionary<TKey, TValue> 类

    ConcurrentDictionary<TKey, TValue> 类 [表示可由多个线程同时访问的键/值对的线程安全集合.] 支持 .NET Framework 4.0 及以上. 示例 ...

  3. Net线程安全集合

    在看Supersocket源码的时候发现很多地方都用到了我们不是很常用的线程安全集合,这些都是由net优化后的线程安全集合因此 应该比我们常规lock来效率好一些 比如说: 1 CurrentStac ...

  4. 通过Collections将集合转换为线程安全类集合

    通过Collections将集合转换为线程安全类集合 List集合: List<String> list=new ArrayList<String>();list.add(&q ...

  5. .Net 线程安全集合

    .Net 提供了基于生产-消费模式的集合类,这些集合对多线程访问安全,定义在System.Collections.Concurrent名称空间中.这个名称空间中包括基础接口IProduceConsum ...

  6. Concurrent下的线程安全集合

    1.ArrayBlockingQueue ArrayBlockingQueue是由数组支持的线程安全的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序.这是一个典型的“有界缓存区”,固定 ...

  7. Java创建多线程和线程安全集合Vector

    public class Test { public static Vector<String> data = new Vector<String>(); public sta ...

  8. 在WPF 4.5中跨线程更新集合

    WPF中一个非常强大的功能是数据绑定,我们可以把一个集合绑定到ListBox中,当集合的数据发生变更时,ListBox界面也会同步变更.本身这是一个非常美好的事情,但是美中不足的是:当把集合绑定到Li ...

  9. 最全java多线程总结3——了解阻塞队列和线程安全集合不

      看了前两篇你肯定已经理解了 java 并发编程的低层构建.然而,在实际编程中,应该经可能的远离低层结构,毕竟太底层的东西用起来是比较容易出错的,特别是并发编程,既难以调试,也难以发现问题,我们还是 ...

随机推荐

  1. Lintcode12-Min Stack-Easy

    2. Min Stack Implement a stack with following functions: push(val) push val into the stack pop() pop ...

  2. HDU 2612 (2次BFS,有点小细节)

    Problem Description Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. L ...

  3. collections.deque

    d = collections.deque([])  # 创建双端队列d.append('a') # 在最右边添加一个元素,此时 d=deque('a')d.appendleft('b') # 在最左 ...

  4. grade配置添加java库导致报 java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMerger

    原因是导入的第三方库中也引入了项目中存在的相同名称的库,导致产生冲突

  5. mapping values are not allowed in this context at line 115 column 10

    /opt/vagrant/embedded/lib/ruby//psych.rb::in `parse': (<unknown>): mapping values are not allo ...

  6. EBS 请求输出Html报表集成Echarts

    百度开源的ECharts有样式丰富且美观的报表类型可供选用,可以将其集成至EBS请求输出的Html报表中,这其实就是一个生成Html数据的过程. 定义输出类型为HTML的请求我就不在此处赘述.   我 ...

  7. 微信小程序常见问题

    上拉加载与下拉刷新 https://blog.csdn.net/yelin042/article/details/71435628 微信小程序---报错:对应的服务器TLS为TLS 1.0,小程序要求 ...

  8. js获取谷歌浏览器版本

    根据浏览器的useragent获取浏览器信息 // 获取谷歌浏览器版本 function getChromeVersion() { var arr = navigator.userAgent.spli ...

  9. Win10系列:C#应用控件基础18

    WebView控件 使用WebView控件可以在应用中添加一个简易的网页浏览器窗口,将指定地址的网页内容显示出来,并可以通过WebView控件所提供的方法.属性及事件,实现如页面导航.HTML文本解析 ...

  10. jquery的一个小扩展,读取URL里的参数

    /* *扩展jquery的功能: *读取url中参数的功能,方法的参数是要获取的URL里键; *使用前要先导入jquery.js文件. * */ (function ($) { $.getUrlPar ...