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语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...
随机推荐
- POJ 2886 Who Gets the Most Candies?(线段树·约瑟夫环)
题意 n个人顺时针围成一圈玩约瑟夫游戏 每一个人手上有一个数val[i] 開始第k个人出队 若val[k] < 0 下一个出队的为在剩余的人中向右数 -val[k]个人 val[k ...
- jQuery插件AjaxFileUpload实现ajax文件上传时老是运行error方法 问题原因
今天在用jQuery插件AjaxFileUpload实现ajax文件上传时,遇到一个问题,如图: 老是运行error.无法运行succes方法,追踪ajaxfileupload.js源代码发现: wa ...
- 【2047】求前n个完全数
Time Limit: 10 second Memory Limit: 2 MB 问题描述 完全数又称完数.完美数.完备数,是一些特殊的自然数,它所有真因子(即除自己以外的因子)的和等于它本身.例如: ...
- LIVE555源代码研究之四:MediaServer (一)
LIVE555源代码研究之四:MediaServer (一) 从本篇文章開始我们将从简单server程序作为突破点,深入研究LIVE555源代码. 从前面的文章我们知道.不论什么一个基于LIVE555 ...
- hadoop配置文件的加载机制 分类: A1_HADOOP 2015-01-21 11:29 839人阅读 评论(0) 收藏
hadoop通过Configuration类来保存配置信息 1.通过Configuration.addResource()来加载配置文件 2.通过Configuration.get***()来获取配置 ...
- mha && 脚本拉起
ip分布: vip 192.168.238.222 mha_manager 192.168.238.131 监控服务器 master 192.168.238.128 主库GTID复制模式 slave ...
- NYOJ 364 田忌赛马
田忌赛马 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描写叙述 Here is a famous story in Chinese history. "That ...
- Eclipse 快捷键大全 分类: C_OHTERS 2014-06-01 13:05 332人阅读 评论(0) 收藏
精选常用: 1. ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了.这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如a ...
- java用volatile或AtomicBoolean实现高效并发处理 (只初始化一次的功能要求)
最近碰到一个这样的功能要求:怎么在一个类里面,实现高效并发处理下只可以初始化一次的方法? 实现方式: 1)volatile方式: /** * Created by Chengrui on 2015/7 ...
- 在线算法与离线算法(online or offline)
1. 在线算法(online) PFC(prefix-free code)编码树的解码过程:可以在二进制编码串的接收过程中实时进行,而不必等到所有比特位都到达后才开始: 2. 离线算法(offline ...