本系列学习在.NET中的并发并行编程模式,实战技巧

本小节了解TPL Dataflow并行工作流,在工作中如何利用现成的类库处理数据。旨在通过TDF实现数据流的并行处理。

TDF Block

数据流由一个一个的块组成,一个块处理完毕后链接到下一个块上。每一个块以消息的形式接收和缓存来自一个或多个源的数据,当接收到信息时,块通过将其行为应用于输入来作出反应,块的输出将传递到下一个块中。

TDF并不是作为.NET4.5框架的一部分分发,需要单独安装,用过nuget导入Microsoft.Tpl.Dataflow。4.5之上在System.Threading.Tasks.Dataflow类库中。TDF提供了一组丰富的组件(块),用于基于进程内消息传递语义来组合数据流和管道基础设施。

TDF最常用的块是标准的BufferBlock、ActionBlock和TransformBlock。它们每个都基于一个委托,该委托可以是匿名函数的形式,用于定义要计算的工作。

BufferBlock<TInput>

BufferBlock是一个很好的工具,用于启用和实现异步生产者/消费者模式,其中内部的消息队列可以由多个源写入或从多个目标读取。保证先进先出的顺序。

以下展示基于TDF BufferBlock的生产者消费者模式

BufferBlock<int> buffer = new BufferBlock<int>(); 
async Task Producer(IEnumerable<int> values)
{
    foreach (var value in values)
        await buffer.SendAsync(value);    
    buffer.Complete();         
}
async Task Consumer(Action<int> process)
{
    while (await buffer.OutputAvailableAsync()) 
        process(await buffer.ReceiveAsync());   
}
public async Task Run()
{
    IEnumerable<int> range = Enumerable.Range(0, 100);
    await Task.WhenAll(Producer(range), Consumer(n =>
        Console.WriteLine($"value {n}")));
}

IEnumerable值的条目通过buffer.Post方法发送到BufferBlock缓冲区,并使用buffer.ReceiveAsync方法异步检索它们。OutputAvailableAsync方法用于当下一个条目准备好可被检索时发出通知。

TransformBlock<TInput,TOutput>

用于映射转换,该转换函数以委托Func<TInput,TOutput>的形式作为参数传递

给定一组地址下载图片为例

var fetchImageFlag = new TransformBlock<string, (string, byte[])>(
    async urlImage =>
    { 
        using (var webClient = new WebClient())
        {
            byte[] data = await webClient.DownloadDataTaskAsync(urlImage); 
            return (urlImage, data);
        }  
    });
List<string> urlFlags = new List<string>{
    "Italy#/media/File:Flag_of_Italy.svg",
    "Spain#/media/File:Flag_of_Spain.svg",
    "United_States#/media/File:Flag_of_the_United_States.svg"
    };
foreach (var urlFlag in urlFlags)
    fetchImageFlag.Post($"https://en.wikipedia.org/wiki/{urlFlag}");

TransformBlock<string, (string, byte[]) 块以元组字符串和字节数组格式来提取标记图像。转换得到字节数组对象后,此处还没有消费使用。下面通过另一个块组合将其保存到本地。

ActionBlock<TInput>

通过名称就可以看出,该块用于接收数据时调用一个委托去处理。因为它没有输出,所以通常用于工作流的结束节点上。

前面通过转换块将图片地址下载转换成了字节数组,下面通过ActionBlock将其持久化本地。

var saveData = new ActionBlock<(string, byte[])>(async data =>
{
    (string urlImage, byte[] image) = data; 
    string filePath = urlImage.Substring(urlImage.IndexOf("File:") + 5);
    await Agents.File.WriteAllBytesAsync(filePath, image); 
});
fetchImageFlag.LinkTo(saveData);    

ActionBlock块实例化传递给构造函数的参数可以是委托Action或Func<TInput,Task>。后者对每个消息输入异步执行内部操作。最后ActionBlock块saveData使用LinkTo扩展方法连接到前面的TransformBlock块上。通过这种方式,TransformBlock生成的输出会在可用时被立即推送到ActionBlock中。

最后粘贴一下File的扩展方法,用于异步读写文件。

public static class File
{
    public static async Task<string[]> ReadAllLinesAsync(string path)
    {
        using (var sourceStream = new FileStream(path,
            FileMode.Open, FileAccess.Read, FileShare.None,
            bufferSize: 4096, useAsync: true))
        using (var reader = new StreamReader(sourceStream))
        {
            var fileText = await reader.ReadToEndAsync();
            return fileText.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
        }
    }
    public static async Task WriteAllTextAsync(string path, string contents)
    {
        byte[] encodedText = Encoding.Unicode.GetBytes(contents);
        await WriteAllBytesAsync(path, encodedText);
    }
    public static async Task WriteAllBytesAsync(string path, byte[] bytes)
    {
        using (var sourceStream = new FileStream(path,
            FileMode.Append, FileAccess.Write, FileShare.None,
            bufferSize: 4096, useAsync: true))
        {
            await sourceStream.WriteAsync(bytes, 0, bytes.Length);
        };
    }
}

ending

第一次做人,何不痛痛快快,潇潇洒洒,讨好自己

工作认认真真的完成,生活充充实实的过着

.NET并发编程-TPL Dataflow并行工作流的更多相关文章

  1. 细说并发编程-TPL

    本节导航 基本概念 并发编程 TPL 线程基础 windows为什么要支持线程 线程开销 CPU的发展 使用线程的理由 如何写一个简单Parallel.For循环 数据并行 Parallel.For剖 ...

  2. c#中@标志的作用 C#通过序列化实现深表复制 细说并发编程-TPL 大数据量下DataTable To List效率对比 【转载】C#工具类:实现文件操作File的工具类 异步多线程 Async .net 多线程 Thread ThreadPool Task .Net 反射学习

    c#中@标志的作用   参考微软官方文档-特殊字符@,地址 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/toke ...

  3. 并发编程-TPL

    并发编程-TPL 本节导航 基本概念 并发编程 TPL 线程基础 windows为什么要支持线程 线程开销 CPU的发展 使用线程的理由 如何写一个简单Parallel.For循环 数据并行 Para ...

  4. C#并发编程之初识并行编程

    写在前面 之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容,因为手中商城的重构工作量较大,一时之间无法抽出时间.近日,这套系统已有阶段性成果,所以准备写一下Parallel ...

  5. .NET并发编程-任务函数并行

    本系列学习在.NET中的并发并行编程模式,实战技巧 请问普通: 被门夹过的核桃还能补脑吗 本小节开始学习基于任务的函数式并行.本系列保证最少代码呈现量,虽然talk is cheap, show me ...

  6. .NET的并发编程(TPL编程)是什么?

    写在前面 优秀软件的一个关键特征就是具有并发性.过去的几十年,我们可以进行并发编程,但是难度很大.以前,并发性软件的编写.调试和维护都很难,这导致很多开发人员为图省事放弃了并发编程.新版 .NET 中 ...

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

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

  8. .NET Core 中的并发编程

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

  9. c#并发编程经典实例文摘

    第1章 并发编程概述 1.1 并发编程简介 并发: 多线程(包括并行处理) 异步编程(异步操作)程序启动一个操作,而该操作将会在一段时间后完成 响应时编程(异步事件)可以没有一个实际的开始,可以在任何 ...

随机推荐

  1. 【Azure API 管理】从微信小程序访问APIM出现200的空响应的问题中发现CORS的属性[terminate-unmatched-request]功能

    问题描述 使用微信小程序调用APIM(API Management)中的接口,发现POST和PUT请求被拦截,返回的状态码为200,但是无消息内容. 在小程序中的调用JS代码如: 通过浏览器测试得到的 ...

  2. 「视频小课堂」ELK和Kafka是怎么就玩在一起成了日志采集解决方案文字版

    视频地址:ELK和Kafka是怎么就玩在一起成了日志采集解决方案 视频文字版 今天呢我就带来了一期视频,主要就是讲ELK和Kafka之间的通讯关系通过对一张通讯图,和一些操作命令,让我们能更深入的去理 ...

  3. linux 安装FastFdfs

    一.安装依赖软件和类库(安装前的准备) 依次执行以下命令: yum install gcc-c++ -y yum -y install zlib zlib-devel pcre pcre-devel ...

  4. 学习Python的书籍——入门到进阶

    入门读物 <Python基础教程>(Beginning Python From Novice to Professional) <Python学习手册>(Learning Py ...

  5. 最短路径(Floyd算法)

    声明:图片及内容基于https://www.bilibili.com/video/BV1oa4y1e7Qt?from=articleDetail 多源最短路径的引入 Floyd算法 原理 加入a: 加 ...

  6. 使用CSS3中Canvas 实现两张图片合成一张图片【常用于合成二维码图片】

    CSS3 Canvas 实现两张图片合成一张图片 需求 需求:在项目中遇到将一张固定图片和一张二维码图片合成一张新图片,并且用户能够将图片保存下载到本地. 思路:使用 CSS3 中的 Canvas 将 ...

  7. 鹏城杯_2018_treasure

    鹏城杯_2018_treasure 首先检查一下保护: IDA分析 我们先来看看settreasure()函数 申请了两个内存空间,并往sea中复制了shellcode 看看这个shellcode,不 ...

  8. JVM学习笔记(二):JVM基本结构

    1 来源 来源:<Java虚拟机 JVM故障诊断与性能优化>--葛一鸣 章节:第二章 本文是第二章的一些笔记整理. 2 JVM基本参数-Xmx java命令的一般形式如下: java [- ...

  9. 一个Bug,让我发现了 Java 界的.AJ(锥)!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 话我放这,踩过的坑越多头发越少! 说来也是奇怪,只要是学编程的,从初次接触的 Jav ...

  10. day12.函数其它与模块1

    一.函数递归 函数的递归调用:是函数嵌套调用的一种特殊形式 具体指的是在调用一个函数的过程中又直接或者间接地调用自己,称之为函数的递归调用 函数的递归调用其实就是用函数实现的循环 # def f1() ...