在上一篇文章演示了并行的流水线操作(生产者和消费者并行同时执行),C#是通过BlockingCollection这个线程安全的对象作为Buffer,并且结合Task来实现的。但是上一篇文章有个缺陷,在整个流水线上,生产者和消费者是唯一的。本文将演示多个消费者多个生产者同时并行执行。

一、多消费者、多生产者示意图

与前一篇文章演示的流水线思想类似,不同之处就是本文的topic:消费者和生产者有多个,以buffer1为例,起生产者有两个,消费者有两个,现在有三个纬度的并行:

  1. Action1和Action2并行(消费者和生产者并行)
  2. 消费者并行(Action2.1和Action2.2并行)
  3. 生产者并行(Action1.1和Action1.2并行)

二、实现

2.1 代码

  1. class PiplelineDemo
  2. {
  3. PRivate int seed;
  4. public PiplelineDemo()
  5. {
  6. seed = 10;
  7. }
  8.  
  9. public void Action11(BlockingCollection<string> output)
  10. {
  11. for (var i = 0; i < seed; i++)
  12. {
  13. output.Add(i.ToString());//initialize data to buffer1
  14. }
  15. }
  16.  
  17. public void Action12(BlockingCollection<string> output)
  18. {
  19. for (var i = 0; i < seed; i++)
  20. {
  21. output.Add(i.ToString());//initialize data to buffer1
  22. }
  23. }
  24.  
  25. public void Action21(BlockingCollection<string> input, BlockingCollection<string> output)
  26. {
  27. foreach (var item in input.GetConsumingEnumerable())
  28. {
  29. var itemToInt = int.Parse(item);
  30. output.Add((itemToInt * itemToInt).ToString());// add new data to buffer2
  31. }
  32. }
  33.  
  34. public void Action22(BlockingCollection<string> input, BlockingCollection<string> output)
  35. {
  36. foreach (var item in input.GetConsumingEnumerable())
  37. {
  38. var itemToInt = int.Parse(item);
  39. output.Add((itemToInt * itemToInt).ToString());// add new data to buffer2
  40. }
  41. }
  42.  
  43. public void Action31(BlockingCollection<string> input, BlockingCollection<string> output)
  44. {
  45. foreach (var item in input.GetConsumingEnumerable())
  46. {
  47. output.Add((item));// add new data to buffer3
  48. }
  49. }
  50.  
  51. public void Action32(BlockingCollection<string> input, BlockingCollection<string> output)
  52. {
  53. foreach (var item in input.GetConsumingEnumerable())
  54. {
  55. output.Add((item));// add new data to buffer3
  56. }
  57. }
  58. public void Pipeline()
  59. {
  60. var buffer1 = new BlockingCollection<string>(seed * 2);
  61. var buffer2 = new BlockingCollection<string>(seed * 2);
  62. var buffer3 = new BlockingCollection<string>(seed * 2);
  63. var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None);
  64. var stage11 = taskFactory.StartNew(() => Action11(buffer1));
  65. var stage12 = taskFactory.StartNew(() => Action12(buffer1));
  66. Task.Factory.ContinueWhenAll(new Task[] { stage11, stage12 }, (tasks) =>
  67. {
  68. buffer1.CompleteAdding();
  69. });
  70. var stage21 = taskFactory.StartNew(() => Action21(buffer1, buffer2));
  71. var stage22 = taskFactory.StartNew(() => Action22(buffer1, buffer2));
  72. Task.Factory.ContinueWhenAll(new Task[] { stage21, stage22 }, (tasks) =>
  73. {
  74. buffer2.CompleteAdding();
  75. });
  76. var stage31 = taskFactory.StartNew(() => Action31(buffer2, buffer3));
  77. var stage32 = taskFactory.StartNew(() => Action32(buffer2, buffer3));
  78. Task.Factory.ContinueWhenAll(new Task[] { stage31, stage32 }, (tasks) =>
  79. {
  80. buffer3.CompleteAdding();
  81. });
  82. Task.WaitAll(stage11, stage12, stage21, stage22, stage31, stage32);
  83. foreach (var item in buffer3.GetConsumingEnumerable())//print data in buffer3
  84. {
  85. Console.WriteLine(item);
  86. }
  87. }
  88. }

2.2 运行结果

2.3 代码解释

  1. Action11和Action12相对比较好理解。初始化数据到buffer1。
  2. Action2.1和Action2.2相对比较费解,他们同时接受buffer1作为输入,为什么最终的结果Buffer2没有产生重复? 最后由Action21,action22同时产生的buffer3为什么也没有重复?这就是GetConsumingEnumerable这个方法的功劳。这个方法会将buffer的数据分成多份给多个消费者,如果一个value已经被一个消费者获取,那么其他消费者将不会再拿到这个值。这就回答了为什么没有重复这个问题。
  3. 上面方法同时使用了多任务延续(ContinueWhenAll)对buffer的调用CompleteAdding方法:该方法非常重要,如果没有调用这个方法,程序会进入死锁,因为消费者(consumer)会处于一直的等待状态。

ParallelProgramming-多消费者,多生产者同时运行并行的更多相关文章

  1. Parallel Programming-多消费者,多生产者同时运行并行

    在上一篇文章演示了并行的流水线操作(生产者和消费者并行同时执行),C#是通过BlockingCollection这个线程安全的对象作为Buffer,并且结合Task来实现的.但是上一篇文章有个缺陷,在 ...

  2. Java多线程消费者、生产者的基本思路

    多线程主要考察的就是 线程的同步控制   生产者消费者的思路就是,当 一个线程执行时让另一个线程 挂起就行了 ThreadOne.ThreadTwo同时运行,添加一个变量在一个公共类(下边的Funct ...

  3. springcloud 实现简单的 消费者和生产者 模式(Restfule 的风格)

    一.springcloud 实现简单的 消费者和生产者 模式(Restfule 的风格) 1.实现简单的消费者和生产者 springcloud使用的http协议进行传输数据,也就是说springclo ...

  4. Java程序设计之消费者和生产者

    新建一个Break类,表示食物数量. public class Break { public static final int MAX = 10; //最多一次性煮十个面包 Stack<Inte ...

  5. java多线程-消费者和生产者模式

    /* * 多线程-消费者和生产者模式 * 在实现消费者生产者模式的时候必须要具备两个前提,一是,必须访问的是一个共享资源,二是必须要有线程锁,且锁的是同一个对象 * */ /*资源类中定义了name( ...

  6. python_并发编程——消费者和生产者模型

    消费者和生产者模型 from multiprocessing import Process,Queue import time import random class Producer(Process ...

  7. 消费者与生产者---LinkedList方式模拟

    采用LinkedList数据结构方式来模拟消费者与生产者模型,小Demo import java.util.LinkedList; public class MyQueue { private fin ...

  8. java并发:初探消费者和生产者模式

    消费者和生产者模式 用继承Thread方式,用wait和notifyAll方法实现. 消费者和生产者模式的特点 1. 什么时候生产:仓库没有满的时候,生产者这可以生产,消费者也可以消费,仓库满的时候停 ...

  9. RabbitMQ消息队列之二:消费者和生产者

    在使用RabbitMQ之前,需要了解RabbitMQ的工作原理. RabbitMQ的工作原理 RabbitMQ是消息代理.从本质上说,它接受来自生产者的信息,并将它们传递给消费者.在两者之间,它可以根 ...

随机推荐

  1. 第三方库SDWebImage的原理

    关于SDWebImage,其实是不用懂原理的,只是有一些面试官会问,分享给正在找工作的朋友们: 不多说直接上图: 另外..... 我的愿望是....... 世界和平.........

  2. 1.tornado实现高并发爬虫

    from pyquery import PyQuery as pq from tornado import ioloop, gen, httpclient, queues from urllib.pa ...

  3. 【SQL】视图

    一.虚拟视图 由create table定义的表:以物理形式存在,实际存储在数据库中 视图:虚拟的,并不是一个真正存在的表 1.视图定义 CREATE VIEW <视图名> AS < ...

  4. pytest学习(3)

    在pytest 2.0以上的版本里,我们也可以通过python -m pytest ...来调用.这实际上和pytest ...几乎一摸一样. 只是用python的时候,把当面目录也加入到sys.pa ...

  5. php关于private、protected、public的区别

    一句话总结: private 自己的 protected 父亲的 public 大众的

  6. 《javascript高级程序设计》读书小延伸

    这本书已经读了几章了,想着试试能不能做出点东西,就简单的练了把手.觉得对于初学者,自己试着练练,效果还不错的. 挥刀要从轻的开始,起初的原因是和同事谈起曾经的逝水年华(小时候干的坏事)时说起了曾经的一 ...

  7. 详解Oracle数据货场中三种优化:分区、维度和物化视图

    转 xiewmang 新浪博客 本文主要介绍了Oracle数据货场中的三种优化:对分区的优化.维度优化和物化视图的优化,并给出了详细的优化代码,希望对您有所帮助. 我们在做数据库的项目时,对数据货场的 ...

  8. HDU 1236 排名(结构体+排序)

    今天的上机考试虽然有实时的Ranklist,但上面的排名只是根据完成的题数排序,没有考虑 每题的分值,所以并不是最后的排名.给定录取分数线,请你写程序找出最后通过分数线的 考生,并将他们的成绩按降序打 ...

  9. python——入门系列(一)索引与切片

    1.索引和切片:python当中数组的索引和其他语言一样,从0~n-1,使用索引的方法也是中括号,但是python中的切片的使用简化了代码 索引:取出数组s中第3个元素:x=s[2] 切片:用极少的代 ...

  10. HDU 2829 Lawrence

    $dp$,斜率优化. 设$dp[i][j]$表示前$i$个数字切了$j$次的最小代价.$dp[i][j]=dp[k][j-1]+p[k+1][i]$.观察状态转移方程,可以发现是一列一列推导出来的.可 ...