• TPL Dataflow并行工作流


    TPL Dataflow并行工作流

    本系列学习在.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

    作者:Leo_wl
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    版权信息
  • 相关阅读:
    from fake_useragent import UserAgent
    teamviewer 安装 仅学习
    利用pandas 中的read_html 获取页面表格
    第十二天 最恶心的考试题
    第十三天 生成器和生成器函数, 列表推导式
    第十一天 函数名的使用以及第一类对象, 闭包, 迭代器
    第十天 动态参数,名称空间,作用域,函数的嵌套,gloabal / nonlocal 关键字
    初始函数, 函数的定义,函数名,函数体以及函数的调用,函数的返回值,函数的参数
    第八天 文件的读,写,追加,读写,写读,seek()光标的移动,修改文件以及另一种打开文件的方式
    第七天 1.基础数据类型的补充 2.set集合 3.深浅拷贝
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/15064190.html
Copyright © 2020-2023  润新知