对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行访问。经常要做的就是对一些队列进行加锁-解锁,然后执行类似插入,删除等等互斥操作。 .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#中的并发编程知识二的更多相关文章

  1. C#中的并发编程知识

      = 导航   顶部 线程 Task async/await IAsyncResult Parallel 异步的回调   顶部 线程 Task async/await IAsyncResult Pa ...

  2. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors

    [Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...

  3. Pthread 并发编程(二)——自底向上深入理解线程

    Pthread 并发编程(二)--自底向上深入理解线程 前言 在本篇文章当中主要给大家介绍线程最基本的组成元素,以及在 pthread 当中给我们提供的一些线程的基本机制,因为很多语言的线程机制就是建 ...

  4. Python中的并发编程

    简介 我们将一个正在运行的程序称为进程.每个进程都有它自己的系统状态,包含内存状态.打开文件列表.追踪指令执行情况的程序指针以及一个保存局部变量的调用栈.通常情况下,一个进程依照一个单序列控制流顺序执 ...

  5. [翻译]在 .NET Core 中的并发编程

    原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 C ...

  6. 并发编程(二)concurrent 工具类

    并发编程(二)concurrent 工具类 一.CountDownLatch 经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作. import java.util.concurren ...

  7. .NET Core 中的并发编程

    今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点. 然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序 ...

  8. Go中的并发编程和goroutine

    并发编程对于任何语言来说都不是一件简单的事情.Go在设计之初主打高并发,为使用者提供了goroutine,使用的方式虽然简单,但是用好却不是那么容易,我们一起来学习Go中的并发编程. 1. 并行和并发 ...

  9. Go语言中的并发编程

    并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...

随机推荐

  1. POJ 2886 Who Gets the Most Candies?(线段树&#183;约瑟夫环)

    题意  n个人顺时针围成一圈玩约瑟夫游戏  每一个人手上有一个数val[i]   開始第k个人出队  若val[k] < 0 下一个出队的为在剩余的人中向右数 -val[k]个人   val[k ...

  2. jQuery插件AjaxFileUpload实现ajax文件上传时老是运行error方法 问题原因

    今天在用jQuery插件AjaxFileUpload实现ajax文件上传时,遇到一个问题,如图: 老是运行error.无法运行succes方法,追踪ajaxfileupload.js源代码发现: wa ...

  3. 【2047】求前n个完全数

    Time Limit: 10 second Memory Limit: 2 MB 问题描述 完全数又称完数.完美数.完备数,是一些特殊的自然数,它所有真因子(即除自己以外的因子)的和等于它本身.例如: ...

  4. LIVE555源代码研究之四:MediaServer (一)

    LIVE555源代码研究之四:MediaServer (一) 从本篇文章開始我们将从简单server程序作为突破点,深入研究LIVE555源代码. 从前面的文章我们知道.不论什么一个基于LIVE555 ...

  5. hadoop配置文件的加载机制 分类: A1_HADOOP 2015-01-21 11:29 839人阅读 评论(0) 收藏

    hadoop通过Configuration类来保存配置信息 1.通过Configuration.addResource()来加载配置文件 2.通过Configuration.get***()来获取配置 ...

  6. mha && 脚本拉起

    ip分布:  vip 192.168.238.222 mha_manager 192.168.238.131 监控服务器 master 192.168.238.128 主库GTID复制模式 slave ...

  7. NYOJ 364 田忌赛马

    田忌赛马 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描写叙述 Here is a famous story in Chinese history. "That ...

  8. Eclipse 快捷键大全 分类: C_OHTERS 2014-06-01 13:05 332人阅读 评论(0) 收藏

      精选常用: 1.  ctrl+shift+r:打开资源 这可能是所有快捷键组合中最省时间的了.这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如a ...

  9. java用volatile或AtomicBoolean实现高效并发处理 (只初始化一次的功能要求)

    最近碰到一个这样的功能要求:怎么在一个类里面,实现高效并发处理下只可以初始化一次的方法? 实现方式: 1)volatile方式: /** * Created by Chengrui on 2015/7 ...

  10. 在线算法与离线算法(online or offline)

    1. 在线算法(online) PFC(prefix-free code)编码树的解码过程:可以在二进制编码串的接收过程中实时进行,而不必等到所有比特位都到达后才开始: 2. 离线算法(offline ...