C#编程(五十八)----------并行集合
并行集合
对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问.经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作. .NET4提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度.
并行集合的命名空间:System.Collections.Concurrent
并行容器:
ConcurrentQueue
ConcurrentStack
ConcurrentBag: 一个无序的数据结构集,当不考虑顺序时非常有用.
BlockingCollection:与经典的阻塞队列数据结构类似
ConcurrentDictoinary
以上这些集合在某种程度上使用了无锁技术(CAS和内存屏蔽),与加互斥锁相比获得了性能的提升.但是在串行程序中,最好不要使用这些集合,他们必然会影响性能.
ConcurrentQueue用法与实例
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作.
Enqueue:在队尾插入元素
TryDequeue:尝试删除对头元素,并通过out参数返回
TryPeek:尝试将对头元素通过out参数返回,但不删除元素
案例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace 并行结合Queue
{
class Program
{
internal static ConcurrentQueue<int> _TestQueue;
class ThreadWork1 //生产者
{
public ThreadWork1()
{ }
public void run()
{
Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
Console.WriteLine("ThreadWork1 producer: "+i);
_TestQueue.Enqueue(i);
}
Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2 //consumer
{
public ThreadWork2()
{ }
public void run()
{
int i = 0;
bool IsDequeue = false;
Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeue = _TestQueue.TryDequeue(out i);
if (IsDequeue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====");
}
if (i==99)
{
break;
}
}
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();
}
}
}
ConcurrentStact
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作.
Push:向栈顶插入元素
TryPop:从栈顶弹出元素,并且通过out参数返回
TryPeek:返回栈顶元素,但不弹出
案例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace 并行结合Queue
{
class Program
{
internal static ConcurrentStack<int> _TestStack;
class ThreadWork1 //生产者
{
public ThreadWork1()
{ }
public void run()
{
Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
Console.WriteLine("ThreadWork1 producer: "+i);
_TestStack.Push(i);
}
Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2 //consumer
{
public ThreadWork2()
{ }
public void run()
{
int i = 0;
bool IsDequeue = false;
Console.WriteLine("ThreadWork2 run { ");
for (; ; )
{
IsDequeue = _TestStack.TryPop(out i);
if (IsDequeue)
{
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====" + i);
}
if (i==99)
{
break;
}
}
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();
}
}
}
ConcurrentBag
一个无序的集合,程序可以向其中插入元素,或删除元素.
在同一个线程中向集合插入,删除元素效率很高.
Add:向集合中插入元素
TryTake:从集合中取出元素并删除
TryPeek:从集合中取出元素,但不删除元素
案例:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 并行集合ConcurrentBag
{
class Program
{
internal static ConcurrentBag<int> _TestBag;
class ThreadWork1 //producer
{
public ThreadWork1()
{ }
public void run()
{
Console.WriteLine("Threadwork1 run { ");
for (int i = 0; i < 100; i++)
{
Console.WriteLine("ThreadWork1 producer: "+i);
_TestBag.Add(i);
}
Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2//consumer
{
public ThreadWork2()
{ }
public void run()
{
bool IsDequeue = false;
Console.WriteLine("ThreadWork2 run { ");
for (int i = 0; i < 100; i++)
{
IsDequeue = _TestBag.TryTake(out i);
if (IsDequeue)
{
Console.WriteLine("ThreadWork2 consumer: " + i * i + "======" + i);
}
}
Console.WriteLine("ThreadWork2 run } ");
}
}
static void Start1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
}
static void Start2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
static void Main(string[] args)
{
Task t1 = new Task(() => Start1());
Task t2 = new Task(() => Start2());
_TestBag = new ConcurrentBag<int>();
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(!__testCollection.IsCompleted)作为退出运行的判断条件.在Work1中,有两条语句被注释了,当i为50时设置为CompleteAdding,但当继续向其中插入元素时,系统抛出异常,提示无法再继续插入.
案例:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 并行结合Collection
{
class Program
{
internal static BlockingCollection<int> _testBCollection;
class ThreadWork1 //producer
{
public ThreadWork1()
{ }
public void run()
{
Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 100; i++)
{
Console.WriteLine("ThreadWork1 producer: "+i);
_testBCollection.Add(i);
//当i为50时设置为CompleteAdding,但当继续向其中插入元素时,系统抛出异常,提示无法再继续插入.
//if (i==50)
//{
// _testBCollection.CompleteAdding();
//}
}
_testBCollection.CompleteAdding();
Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2//consumer
{
public ThreadWork2()
{ }
public void run()
{
int i = 0;
bool IsDequeue = false;
Console.WriteLine("ThreadWork2 run { ");
while (!_testBCollection.IsCompleted)
{
//不明白这里为啥i会自动的++
//Console.WriteLine("i=================="+i);
IsDequeue = _testBCollection.TryTake(out i);
if (IsDequeue)
{
Console.WriteLine("ThreadWork2 consumer: "+i*i+"====="+i);
//i++;这句话不加还是会出现自动++的操作
}
}
}
}
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();
}
}
}
分析://_testBCollection.CompleteAdding();//这句话注释掉work2陷入循环,无法退出
ConcurrentDictionary
对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁.
AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值
GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值.
TryAdd:尝试在容器中添加新的键和值
TryGetValue:尝试根据指定的键获得值
TryUpdate:有条件的更新当前键所对应的值
TryRemove:尝试删除指定的键.
Getenumerator:返回一个能够遍历整个容器的枚举器.
案例:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 并行集合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 = 0; i < 100; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestDictionary.TryAdd(i, i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2//consumer
{
public ThreadWork2()
{ }
public void run()
{
bool IsOk = false;
Console.WriteLine("ThreadWork2 run { ");
int nCnt = 0,i=0,nValue=0;
while (nCnt<100)
{
IsOk = _TestDictionary.TryGetValue(i,out nValue);
if (IsOk)
{
Console.WriteLine("ThreadWork2 consumer: "+i*i+"====="+i);
nValue *= nValue;
nCnt++;
i++;
}
}
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 = 0;
_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);
}
IEnumerator<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();
}
}
}
总结说明: .NET4包含的新命名空间System.Collection,Concurrent有几个线程安全的集合类.线程安全的集合可防止多个线程以相互冲突的方式访问集合.
为了对集合进行线程安全的访问,定义了IPriducerConsumerCollection<T>接口.这个接口中最重要的方法是TryAdd()和TryTake().TryAdd()方法尝试给集合添加一项,但如果集合禁止添加项,这个操作可能失败.为了给出相关信息,TryAdd()方法返回一个布尔值,以说明操作是成功还是失败.TryTake()方法也以这种方式工作,以通过调用者操作是成功还是失败,并在操作成功时返回集合中的项.
C#编程(五十八)----------并行集合的更多相关文章
- 学习ASP.NET Core Razor 编程系列十八——并发解决方案
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- C#编程(五十九)----------集合的性能
各种集合的性能 许多集合类提供了相同的功能,例如,SortedList类与SortedDictionary类的功能几乎完全相同.但是,其性能常常有很大的区别.SortedList集合使用的内存少,So ...
- 五十八、linux 编程——UDP 编程 广播
58.1 广播介绍 58.1.1 介绍 广播实现一对多的通讯 它通过向广播地址发送数据报文实现的 58.1.2 套接字选项 套接字选项用于修饰套接字以及其底层通讯协议的各种行为.函数 setsocko ...
- Coding and Paper Letter(五十八)
资源整理. 1 Coding: 1.支持TMS.WMTS标准瓦片下载,支持百度地图瓦片.高德地图瓦片.腾讯地图瓦片.天地图.ArcServer Rest瓦片.ArcServer本地缓存切片.geose ...
- C#高级编程五十四天----Lookup类和有序字典
Lookup类 Dictionary<Tkey,TValue>仅仅为每一个键支持一个值.新类Lookup<Tkey,TValue>是.NET3.5中新增的,它类似与Dictio ...
- JavaEE基础(十八)/集合
1.集合框架(Map集合概述和特点) A:Map接口概述 查看API可以知道: 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 B:Map接口和Collection接口的不同 ...
- Java开发笔记(五十八)简单接口及其实现
前面介绍了抽象方法及抽象类的用法,看似解决了不确定行为的方法定义,既然叫唤动作允许声明为抽象方法,那么飞翔.游泳也能声明为抽象方法,并且鸡类涵盖的物种不够多,最好把这些行为动作扩展到鸟类这个群体,于是 ...
- 五十八 数据库访问使用SQLite
SQLite是一种嵌入式数据库,它的数据库就是一个文件.由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成. Python就 ...
- python核心编程五——映像和集合
1.字典 不同意一个键相应多个值:当有键发生冲突(即.字典键反复赋值),取最后(近期)的赋值. >>> dict1 = {' foo':789, 'foo': 'xyz'} ...
随机推荐
- CentOS 5.x 键盘布局改为日语
CentOS 5.x 直接在系统设置界面修改键盘布局,可能不起作用,需要按如下步骤修改配置文件. 1. /etc/sysconfig/keyboard KEYTABLE="jp106&quo ...
- 【LOJ】#6433. 「PKUSC2018」最大前缀和
题解 神仙的状压啊QAQ 设一个\(f[S]\)表示数字的集合为\(S\)时\(sum[S]\)为前缀最大值的方案数 \(g[S]\)表示数字集合为\(S\)时所有前缀和都小于等于0的方案数 答案就是 ...
- myBatsi调用存储过程
1.结构 2.准备数据 建表和插入数据 CREATE TABLE p_user( id INT PRIMARY KEY AUTO_INCREMENT, name ), sex ) ); INSERT ...
- word2013 如何设置从第三页开始编码 或 如何设置封面页和正文页页码不连续
首先说明一下 “分节符”作用,它就是用来将整个文档分节的,添加一个分节符,文档就分成1.2两节:添加两个分节符,文档就分成1.2.3节. 当前页面具体是第几节,可以通过点击页眉页脚来查看: 从第三页开 ...
- ionic获取ios唯一设备id的解决方案
经常有朋友来问这个问题. 每次都去解释这个问题也浪费不少时间, 所以还是开一篇文章, 把这个问题说清楚吧. 先纠正一个误区吧: 有同学可以通过ionic natvie的device插件获取. 我们在文 ...
- cv2 与 matplotlib 的 Bug 记录
cv2 的 imread 无法读取中文路径 解决方案: img = cv2.imdecode(np.fromfile(image_path,dtype=np.uint8),cv2.IMREAD_COL ...
- 【教程】Tomcat 的catalina.out 日志按照自定义日期格式进行切割
本文简单介绍在使用cronolog对tomcat的日志进行自定义日期格式的切割,方便日志的整理和遇到问题日志的排查! 安装cronolog 安装cronolog的方法网上有很多,这里也简单的介绍一下. ...
- 小成就之解决调用spring中FileSystemXmlApplicationContext路径问题
此文写下调用spring过程中遇到的一个问题!或许对于入行的人一看觉得我很傻逼吧,这问题谁都会了!但我觉得对于新手(自已)来说,算是一个好思路与好办法! 问题: 对于 test_aa ta = (te ...
- [转]C++ STL list的初始化、添加、遍历、插入、删除、查找、排序、释放
list是C++标准模版库(STL,Standard Template Library)中的部分内容.实际上,list容器就是一个双向链表,可以高效地进行插入删除元素. 使用list容器之前必须加上S ...
- seq2seq模型以及其tensorflow的简化代码实现
本文内容: 什么是seq2seq模型 Encoder-Decoder结构 常用的四种结构 带attention的seq2seq 模型的输出 seq2seq简单序列生成实现代码 一.什么是seq2seq ...