c# 高并发的几种方式(四)

380 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情 

回顾

这是c#并发编程的第四篇文章,前面我们简单了解了三种并发技术,回顾一下:actor模型,异步和并行三种技术。今天我们在介绍一种并发技术,即:数据流编程技术。

简介

c# 的并行库TPL,我们在上一章并发编程中简单的了解过。在任务并行库TPL中,也提供了数据流并行组件。
在数据流处理中,ISourceBlock 和ITargetBlock接口需要引起重视,根据名字,一个是生产者,一个是消费者。
数据流的处理是将数据分为块数据,并进行网格处理。数据在输入端流入,经过处理网格,然后在输出端流出。

Bufferblock

一个简单的fifo,通过post添加数据,通过receive方法阻塞或者异步的获取数据。也可以通过link向其他的block输出数据。

来一个丐版demo

using System;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;

namespace DataFlowDemoS
{
    internal class Program
    {
        //全局buffer
        private static BufferBlock<int> m_buffer = new BufferBlock<int>();
        /// <summary>
        /// 生产者
        /// </summary>
        private static void Producer1()
        {
            for(int loop=0;loop<10;loop++)
            { 
                m_buffer.Post(loop);
            }
        }
        /// <summary>
        /// 消费者
        /// </summary>
        private static void Consumer1()
        {
            while (true)
            {
                int item = m_buffer.Receive();
                Console.WriteLine(item);
            }
        }
        /// <summary>
        /// main
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.WriteLine("Here We Go!");
            var p = Task.Factory.StartNew(Producer1);
            var c = Task.Factory.StartNew(Consumer1);
            Task.WaitAll(p, c);
        }
    }
}

效果

image.png

ActionBlock

actionblock继承于ITargetBlock,是消费数据block,actionblock会处理fifo中的每一个数据,而且每次只可以处理一个数据,可以通过并行来执行多个数据处理。
actionblock的构造函数中,允许输入一个委托来进行数据处理

demo

using System;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;
using System.Threading;

namespace DataFlowDemoS
{
    internal class Program
    {
        /// <summary>
        /// 消费者
        /// </summary>
        public static ActionBlock<int> abSync = new ActionBlock<int>((i) =>
        {
            Thread.Sleep(1000);
            Console.WriteLine(i + " ThreadId:" + Thread.CurrentThread.ManagedThreadId + " Execute Time:" + DateTime.Now);
        });

        /// <summary>
        /// 生产者
        /// </summary>
        public static void TestSync()
        {
            for (int i = 0; i < 10; i++)
            {
                abSync.Post(i);
            }
            Console.WriteLine("生产完毕");
        }

        /// <summary>
        /// main
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.WriteLine("Here We Go!");
            TestSync();
            Console.ReadLine();
        }
    }
}

效果

image.png

数据是异步处理的,生产者生产完毕即退出。消费者在后台处理数据。因为是fifo,也保证了,数据处理的顺序性和准确性。

增加并行处理

actionbloc中提供一些接口,可以让处理数据的线程并行。
我们改造一下之前的消费者,并行3个线程同时处理数据

        /// <summary>
        /// 消费者
        /// </summary>
        public static ActionBlock<int> abSync = new ActionBlock<int>((i) =>
        {
            Thread.Sleep(1000);
            Console.WriteLine(i + " ThreadId:" + 
            Thread.CurrentThread.ManagedThreadId + " Execute Time:" + DateTime.Now);
        }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 3 });
        /// <summary>
        /// 生产者
        /// </summary>
        public static void TestSync()
        {
            for (int i = 0; i < 10; i++)
            {
                abSync.Post(i);
            }
            Console.WriteLine("生产完毕");
            abSync.Complete();
            Console.WriteLine("停止接收数据");
            abSync.Completion.Wait();
            Console.WriteLine("处理完毕");
        }

效果

image.png