C#中的并发编程知识二
=
对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问。经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作。 .NetFramework 4.0 中提供了一些封装好的支持并行操作数据容器,可以减少并行编程的复杂程度。
基本信息
.NetFramework中并行集合的名字空间: System.Collections.Concurrent
并发容器
- ConcurrentQueue
- ConcurrentStack
- ConcurrentDictionary
- ConcurrentBag:一个无序的数据结构集,当不需要考虑顺序时非常有用。
- BlockingCollection与经典的阻塞队列数据结构类似
这些集合在某种程度上使用了无锁技术(CAS Compare-and-Swap和内存屏障Memory Barrier),与加互斥锁相比获得了性能的提升。但在串行程序中,最好不用这些集合,它们必然会影响性能。
ConcurrentQueue
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。
- Enqueue:在队尾插入元素
- TryDequeue:尝试删除队头元素,并通过out参数返回
- TryPeek:尝试将对头元素通过out参数返回,但不删除该元素。
class Program
{
internal static ConcurrentQueue<int> _TestQueue;
class ThreadWork1 // producer
{
public void run()
{
System.Console.WriteLine("ThreadWork1 run { ");
for (int i = 0; i < 30; i++)
{
System.Console.WriteLine("ThreadWork1 producer: " + i);
_TestQueue.Enqueue(i);
}
System.Console.WriteLine("ThreadWork1 run } ");
}
}
class ThreadWork2 // consumer
{
public void run()
{
int i=0;
bool IsDequeuue = false;
System.Console.WriteLine("ThreadWork2 run { ");
for (;; i++)
{
IsDequeuue = _TestQueue.TryDequeue(out i);
if (IsDequeuue)
System.Console.WriteLine("ThreadWork2 consumer: " + i * i + " =====");
if (i == 29)
break;
}
System.Console.WriteLine("ThreadWork2 run } ");
}
}
static void StartT1()
{
ThreadWork1 work1 = new ThreadWork1();
work1.run();
}
static void StartT2()
{
ThreadWork2 work2 = new ThreadWork2();
work2.run();
}
public static void Main()
{
Task t1 = new Task(() => StartT1());
Task t2 = new Task(() => StartT2());
_TestQueue = new ConcurrentQueue<int>();
Console.WriteLine("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("Main }");
Console.ReadKey();
}
}
ConcurrentStack
其完全无锁,但当CAS面临资源竞争失败时可能会陷入自旋并重试操作。
- Push:向栈顶插入元素
- TryPop:从栈顶弹出元素,并且通过out 参数返回
- TryPeek:返回栈顶元素,但不弹出
ConcurrentBag
一个无序的集合,程序可以向其中插入元素,或删除元素。
在同一个线程中向集合插入,删除元素的效率很高。
- Add:向集合中插入元素
- TryTake:从集合中取出元素并删除
- TryPeek:从集合中取出元素,但不删除该元素。
BlockingCollection
一个支持界限和阻塞的容器
- Add:向容器中插入元素
- TryTake:从容器中取出元素并删除
- TryPeek:从容器中取出元素,但不删除。
- CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。
- IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。
要点一:消费者线程完全使用 while (!_TestBCollection.IsCompleted) 作为退出运行的判断条件
要点二:当BlockingCollection对象设置为CompleteAdding(.CompleteAdding()),但当继续向其中插入元素时,系统抛出异常,提示无法再继续插入。
class Program
{
internal static BlockingCollection<int> _TestBCollection;
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);
_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 = 0;
int nCnt = 0;
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();
}
}
ConcurrentDictionary
对于读操作是完全无锁的,当很多线程要修改数据时,它会使用细粒度的锁。
- AddOrUpdate:如果键不存在,方法会在容器中添加新的键和值,如果存在,则更新现有的键和值。
- GetOrAdd:如果键不存在,方法会向容器中添加新的键和值,如果存在则返回现有的值,并不添加新值。
- TryAdd:尝试在容器中添加新的键和值。
- TryGetValue:尝试根据指定的键获得值。
- TryRemove:尝试删除指定的键。
- TryUpdate:有条件的更新当前键所对应的值。
- GetEnumerator:返回一个能够遍历整个容器的枚举器。
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()
{
int i = 0, nCnt = 0;
int nValue = 0;
bool IsOk = false;
System.Console.WriteLine("ThreadWork2 run { ");
while (nCnt < 100)
{
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 = 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 }");
#region 其他
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);
}
#endregion
Console.WriteLine("\n\nDictionary Count: " + _TestDictionary.Count);
Console.WriteLine("Sample 4-5 Main }");
Console.ReadKey();
}
}
C#中的并发编程知识二的更多相关文章
- C#中的并发编程知识
= 导航 顶部 线程 Task async/await IAsyncResult Parallel 异步的回调 顶部 线程 Task async/await IAsyncResult Pa ...
- [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors
[Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...
- Pthread 并发编程(二)——自底向上深入理解线程
Pthread 并发编程(二)--自底向上深入理解线程 前言 在本篇文章当中主要给大家介绍线程最基本的组成元素,以及在 pthread 当中给我们提供的一些线程的基本机制,因为很多语言的线程机制就是建 ...
- Python中的并发编程
简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执 ...
- [翻译]在 .NET Core 中的并发编程
原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 C ...
- 并发编程(二)concurrent 工具类
并发编程(二)concurrent 工具类 一.CountDownLatch 经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作. import java.util.concurren ...
- .NET Core 中的并发编程
今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点. 然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序 ...
- Go中的并发编程和goroutine
并发编程对于任何语言来说都不是一件简单的事情.Go在设计之初主打高并发,为使用者提供了goroutine,使用的方式虽然简单,但是用好却不是那么容易,我们一起来学习Go中的并发编程. 1. 并行和并发 ...
- Go语言中的并发编程
并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...
随机推荐
- php实现 字符串分割
php实现 字符串分割 一.总结 一句话总结: 1.字符串按固定位分割函数是什么? 7 $str_arr=str_split($str,8); 2.字符串填补函数的参数是什么? 10 $str_arr ...
- 【习题5-3 UVA-10935】Throwing cards away I
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用STL的queue写 [代码] #include <bits/stdc++.h> using namespace st ...
- VS2012 打包部署程序
一. 下载 InstallShield 2015(支持VS2012) VS2012没有自带打包工具,所以要先下载并安装一个打包工具.我采用微软提供的打包工具: InstallShield2015 ...
- workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的)
workerman-chat(PHP开发的基于Websocket协议的聊天室框架)(thinkphp也是支持socket聊天的) 一.总结 1.下面链接里面还有一个来聊的php聊天室源码可以学习 2. ...
- 得到INI文件所有Section(所有节点名称)
char SectionNames[MAX_PATH],*pSectionName; ZeroMemory(SectionNames,MAX_PATH); GetPrivateProfileSecti ...
- 使用DatagramSocket与DatagramPacket传输数据 分类: B1_JAVA 2013-10-12 13:00 1936人阅读 评论(0) 收藏
参考传智播客毕向东JAVA视频. 注: DatagramSocket发送的每个包都需要指定地址,而Socket则是在首次创建时指定地址,以后所有数据的发送都通过此socket. A socket is ...
- php实现把二叉树打印成多行(谋而后动,写好算法思路,不然浪费超多时间而且还是错误代码,而且精力消耗会导致代码正确率下降以及低级错误)
php实现把二叉树打印成多行(谋而后动,写好算法思路,不然浪费超多时间而且还是错误代码,而且精力消耗会导致代码正确率下降以及低级错误) 一.总结 要点:a.层次遍历(队列) b.层次遍历中的层次(孩 ...
- centos-mirrors
http://mirrors.aliyun.com/centos/7.2.1511/os/x86_64/Packages/ http://mirrors.aliyun.com/centos/7.2.1 ...
- Windows Phone 8.1 Tiles, Notifications and Action Center
(1)Tiles Tiles 也就是磁贴,是 Windows Phone 的一大特色. 一个 Tile 其实可以看成是一个 XML,比如: <tile> <visual> &l ...
- 结构体(struct)与类(class)
在 C++ 的范畴里,除了 struct 结构体不支持访问权限修饰符(private.protected.public)外,已不再区分二者的其他方面语法上的差异. 因此,在 C++ 编程中,如果一个类 ...