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 6.9 安装Rabbitmq
一.安装Rabbitmq 首先安装编译工具 yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel Er ...
- linux下安装redis及其中遇到的问题的解决方法
1.将下载好的压缩包放到/usr/local目录下# tar xzf redis-3.0.2.tar.gz # cd redis-3.0.2 # make 提示错误 make: cc: Command ...
- tidb 升级步骤
1.检查ansible版本,正常情况下,2.1 rc3需要兼容ansible 2.5以上的版本 $ ansible --version 2.检查python两个模块jinja2需要升级到2.9.6或以 ...
- spark优化之并行度
这个其实我前面已经记录过了,这里在记录一下. 我可以通过参数人为的来控制分区大小,增加分区中即可增加任务的并行度,并行度高自然运行的就快了嘛. 官方推荐集群中每个cpu并行的任务是2-3个(也就是2- ...
- Unit的各种断言
今天遇到这个问题,就值得自己总结一下. 1.介绍 JUnit为我们提供了一些辅助函数,他们用来帮助我们确定被测试的方法是否按照预期的效果正常工作,通常,把这些辅助函数称为断言.下面我们来介绍一下JUn ...
- 怎么去除移动端点击a标签链接时的背景色
在手机端点击链接时会发现有一个背景色,有时候需要改变这个颜色,或者去除这个颜色,有时候a标签做成按钮时这个颜色的存在会很丑,像这样: 可以通过 -webkit-tap-highlight-color: ...
- Ansible专题整理
Ansible 专题文章总览 Ansible小手册,仅供参考 文章如未明确说明实验环境,默认如下: OS:Centos 6.7 x86_64 Ansible: 2.1.2.0 Python: 2.6. ...
- Windows密码破解工具ophcrack
Windows密码破解工具ophcrack Windows用户密码都采用哈希算法加密进行保存.Kali Linux内置了专用破解工具ophcrack.该工具是一个图形化界面工具,支持Windows ...
- Bzoj2534:后缀自动机 主席树启发式合并
国际惯例的题面:考虑我们求解出字符串uvu第一个u的右端点为i,第二个u的右端点为j,我们需要满足什么性质?显然j>i+L,因为我们选择的串不能是空串.另外考虑i和j的最长公共前缀(也就是说其p ...
- 使用NewLife网络库构建可靠的自动售货机Socket服务端(一)
最近有个基于tcp socket 协议和设备交互需求,想到了新生命团队的各种组件,所以决定用NewLife网络库作为服务端来完成一系列的信息交互. 第一,首先说一下我们需要实现的功能需求吧 1,首先客 ...