本系列学习在.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. NumPy 将停止支持 Python 2

    NumPy 项目宣布将停止支持 Python 2.Python 核心团队已经决定在 2020 年停止支持 Python 2,而 NumPy 项目自 2010 年以来同时支持 Python 2 和 Py ...

  2. P2766 最长不下降子序列问题 题解(网络流)

    题目链接 最长不下降子序列问题 解题思路 分成三小问解决. 第一小问,求\(LIS\),因为\(n<=500\),直接\(O(N^2)\)暴力求解即可. 第二三小问,建立模型用网络流求解. 对于 ...

  3. C# 通过ServiceStack 操作Redis——Set类型的使用及示例

    ServiceStack 程序集里面没有方法注解,我在这里将注解添加上去,有不当之处,欢迎指正 Console.WriteLine("---Set类型---"); //添加 set ...

  4. 如何在 ASP.Net Core 中实现 健康检查

    健康检查 常用于判断一个应用程序能否对 request 请求进行响应,ASP.Net Core 2.2 中引入了 健康检查 中间件用于报告应用程序的健康状态. ASP.Net Core 中的 健康检查 ...

  5. python常见错误和异常

    1.BaseExeception 所有异常的基类 2.SystemEixt 解释器请求退出 3.KeyboardInterrupt 用户中断执行 4.Exception 常规错误的基类 5.StopI ...

  6. 基础篇:JAVA引用类型和ThreadLocal

    前言 平时并发编程,除了维护修改共享变量的场景,有时我们也需要为每一个线程设置一个私有的变量,进行线程隔离,java提供的ThreadLocal可以帮助我们实现,而讲到ThreadLocal则不得不讲 ...

  7. 自己挖的坑自己填--jxl进行Excel下载堆内存溢出问题

    今天在进行使用 jxl 进行 Excel 下载时,由于数据量大(4万多条接近5万条数据的下载),数据结构过于负责,存在大量大对象(虽然在对象每次用完都设置为null,但还是存在内存溢出问题),加上本地 ...

  8. shell分支与循环结构

    1. 条件选择 1.1 条件判断分支介绍 格式 if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMM ...

  9. Julia语言程序基础

    Julia-lang 新兴的Julia语言,Julia 一开始就是为高性能而设计的. Julia 程序通过 LLVM 编译成高效的多平台机器码. Julia中文社区: https://cn.julia ...

  10. 字符串函数的实现(三)之strcat

    C语言中的字符串函数有如下这些 获取字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数 strncpy strncat strncmp ...