一、引言

上一篇博客 分享了使用 Channel 来实现针对大量数据的多线程异步处理,感谢大哥们在评论中提出的宝贵的问题和建议!本篇将分享使用 ActionBlock 如何实现,欢迎在评论区留言讨论。

二、ActionBlock介绍

什么是 ActionBlock?

ActionBlock是 .NET 中 TPL Dataflow 库的一部分,用于处理数据流和并行任务。它提供了一种简单而强大的方式来处理并行任务,并且可以轻松地实现生产者-消费者模式。

ActionBlock 的特点

  • 并行处理:ActionBlock可以配置为并行处理多个任务,从而提高处理效率
  • 异步编程:支持异步编程模型,可以避免阻塞线程,提高应用程序的响应速度和吞吐量
  • 数据流控制:可以通过设置最大并行度和其他选项来控制数据流的处理方式
  • 任务调度:可以用于调度和管理并行任务,确保任务按预期执行

ActionBlock 的使用场景

  • 生产者-消费者模式:可以用于实现生产者-消费者模式,其中生产者将数据发送到ActionBlock,消费者从ActionBlock中读取数据并进行处理
  • 数据流处理:适用于需要处理大量数据并且需要并行处理的场景,例如日志处理、数据转换等
  • 任务调度:可以用于调度和管理并行任务,确保任务按预期执行

ActionBlock 的基本用法

使用ActionBlock非常简单,主要步骤如下:

  1. 创建 ActionBlock:定义一个 ActionBlock,指定要执行的操作和并行选项
  2. 发送数据到 ActionBlock:使用SendAsync方法将数据发送到 ActionBlock
  3. 完成 ActionBlock:在所有数据发送完成后,调用Complete方法通知 ActionBlock 不再接收新的数据
  4. 等待处理完成:使用Completion属性等待所有数据处理完成

以下是一个简单的示例代码,展示了如何使用 ActionBlock:

using System.Threading.Tasks.Dataflow;

var actionBlock = new ActionBlock<int>(async item =>
{
// 模拟异步处理
await Task.Delay(100);
Console.WriteLine($"Processed item: {item}");
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 4 // 设置最大并行度
}); // 发送数据到 ActionBlock
for (int i = 0; i < 10; i++)
{
await actionBlock.SendAsync(i);
} // 完成 ActionBlock
actionBlock.Complete();
// 等待处理完成
await actionBlock.Completion; Console.WriteLine("All items processed.");

三、假设场景

假设我们有一组数据需要经过两个步骤的处理。每个数据项都需要进行初步处理,然后进行进一步处理。希望步骤2可以在步骤1产生结果数据后立即开始处理,而不是等待步骤1完全处理完毕。

四、解决方案

使用TransformBlockActionBlock来实现生产者-消费者模式。生产者负责读取数据并将其发送到TransformBlock中,消费者从TransformBlock中读取数据并进行处理。

以下是一个简单的示例代码,演示如何使用TransformBlockActionBlock实现生产者-消费者模式来处理数据:

using System.Threading.Tasks.Dataflow;

var cts = new CancellationTokenSource();
// 假设有一组数据
var dataItems = Enumerable.Range(0, 1000).Select(x => $"data_{x}").ToList(); var processor = new DataProcessor(10, cts.Token);
await processor.ProcessAsync(dataItems); Console.ReadKey(); /// <summary>
/// 数据处理器
/// </summary>
public class DataProcessor(int maxDegreeOfParallelism, CancellationToken cancellationToken)
{
public async Task ProcessAsync(List<string> dataItems)
{
// 创建一个 TransformBlock 用于步骤1的处理,并将结果发送到步骤2的 ActionBlock
var step1Block = new TransformBlock<string, string>(async dataItem => await Step1(dataItem), new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism,
CancellationToken = cancellationToken
}); // 创建一个 ActionBlock 用于步骤2的处理
var step2Block = new ActionBlock<string>(async dataItem =>
{
await Step2(dataItem);
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism,
CancellationToken = cancellationToken
}); // 将 TransformBlock 链接到 ActionBlock
step1Block.LinkTo(step2Block, new DataflowLinkOptions { PropagateCompletion = true }); // 启动多个步骤1的任务(生产者)
foreach (var dataItem in dataItems)
{
await step1Block.SendAsync(dataItem, cancellationToken);
} // 完成步骤1的 TransformBlock 的写入
step1Block.Complete();
// 等待步骤1的 TransformBlock 处理完成
await step1Block.Completion; // 完成步骤2的 ActionBlock 的写入
step2Block.Complete();
// 等待步骤2的 ActionBlock 处理完成
await step2Block.Completion;
} private async Task<string> Step1(string dataItem)
{
// 模拟步骤1的处理(如初步处理数据)
await Task.Delay(10, cancellationToken);
Console.WriteLine($"Step1 processed data item: {dataItem}");
return dataItem;
} private async Task Step2(string dataItem)
{
// 模拟步骤2的处理(如进一步处理数据)
await Task.Delay(10, cancellationToken);
Console.WriteLine($"Step2 processed data item: {dataItem}");
}
}

代码解释:

  1. 创建Step1的 TransformBlock:在ProcessAsync方法中,我们首先创建了一个 TransformBlock,用于Step1的处理,TransformBlock 接受一个输入数据项,进行处理后返回一个输出数据项,TransformBlock<string, string>表示输入和输出都是string类型
  2. 创建Step2的 ActionBlock:创建一个 ActionBlock 用于Step2的处理,ActionBlock 接受一个输入数据项并进行处理,但不返回输出数据项。ActionBlock<string>表示输入是string类型
  3. 链接 TransformBlock 和 ActionBlock:将 TransformBlock 链接到 ActionBlock ,以便将Step1的处理结果发送到Step2进行处理,使用LinkTo方法将两个块连接起来,并设置PropagateCompletion为 true,表示当 TransformBlock 完成时,ActionBlock 也会完成
  4. 启动Step1的任务:逐个将数据项发送到 TransformBlock,并等待所有数据处理完成,使用SendAsync方法将数据项发送到 TransformBlock
  5. 等待任务完成:使用Complete方法通知 TransformBlock 不再接收新的数据,并使用Completion属性等待所有数据处理完成。然后完成Step2的 ActionBlock 的写入,并等待Step2的 ActionBlock 处理完成

在 .NET Core 中使用 ActionBlock 实现高效率的多步骤数据处理的更多相关文章

  1. 在ef core中使用postgres数据库的全文检索功能实战之中文支持

    前言 有关通用的postgres数据库全文检索在ef core中的使用方法,参见我的上一篇文章. 本文实践了zhparser中文插件进行全文检索. 准备工作 安装插件,最方便的方法是直接使用安装好插件 ...

  2. .NET Core 中的日志与分布式链路追踪

    目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...

  3. .net core中Grpc使用报错:The remote certificate is invalid according to the validation procedure.

    因为Grpc采用HTTP/2作为通信协议,默认采用LTS/SSL加密方式传输,比如使用.net core启动一个服务端(被调用方)时: public static IHostBuilder Creat ...

  4. .NET Core中的认证管理解析

    .NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...

  5. ASP.NET Core 中的那些认证中间件及一些重要知识点

    前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...

  6. Asp.net Core中使用Session

    前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...

  7. 在ASP.NET Core中使用百度在线编辑器UEditor

    在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...

  8. ASP.NET Core中的依赖注入(1):控制反转(IoC)

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

  9. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

  10. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

随机推荐

  1. kotlin关键字与操作符

    硬关键字:始终解释为关键字,不能用作标识符 as - 用于类型转换 - 为导入指定一个别名 as? 用于安全类型转换 break 终止循环的执行 class 声明一个类 continue 继续最近层循 ...

  2. kotlin基础——>基本数据类型、控制流、返回和跳转

    1.对于数字的定义,支持java 8的下划线分割的方式 val a = 1_2_3 与 val a = 123 是相同的 2.如果要指定数字类型有两种方式 val a : Float = 1 或者 v ...

  3. (超全)Python汇总篇,200+Python标准库介绍

    关于Python标准库 众所周知,Python是一个依赖强大的组件库完成对应功能的语言,为了便捷实现各项功能,前辈大牛们打造了多种多样的工具库公开提供给大众使用,而越来越多的库已经因为使用的广泛和普遍 ...

  4. gcc各等级优化的性质

    原帖地址:CSDN「隨意的風」gcc 中-O -O1 -O2 -O3 -Os -Ofast -Og优化的原理 一般来说,如果不指定优化标识的话,gcc就会产生可调试代码,每条指令之间将是独立的:可以在 ...

  5. Windows下如何用virtualenv创建虚拟环境

    虚拟环境可以有效的解决不同项目需要不同环境的问题,虚拟环境最大的好处就是可以将我们的开发环境进行隔离,让彼此之间不互相受影响.一.Windows下创建虚拟环境1.虚拟环境需要用到的库是virtuale ...

  6. 需求解决 _按需要对控件进行禁用和解除禁用 _CSS _20210922

    需求解决 _按需要对控件进行禁用和解除禁用 _CSS _20210922 通过JQuery 或者其他方式的选择器 获取DOM节点 再通过一下的方式 设置 disabled属性为 true 即可禁用,为 ...

  7. 2024/9/16 CSP-S模拟赛试题

    A 这题是很有意思的一个题,思路就是你考虑kt的位置只可能在四个角,因为这种情况下,他的距离才会最远对吧,所以你就暴力找另一个人fengwu的点的位置,然后计算他们之间的距离然后你求一个\(\max\ ...

  8. Nginx服务器配置---反向代理服务时proxy_pass的转发规则

    nginx是由俄罗斯开发的一款http web服务器,我们经常用这款服务器做负载均衡和反向代理.今天我们就来聊聊Nginx作为反向代理时,如何进行路由配置.假设你已经部署好Nginx了,我们进入Ngi ...

  9. 开源IDS/IPS Suricata的部署与使用

    目录 前言 在Linux上部署Suricata Suricata的基本配置 配置文件 Suricata的规则 Suricata的使用 Suricata检测SQL注入 前言 Suricata 是一个高性 ...

  10. 解码OutOfMemoryError:PermGen Space

    本文由 ImportNew - Peter Pan 翻译自 javacodegeeks.如需转载本文,请先参见文章末尾处的转载要求. ImportNew注:如果你也对Java技术翻译分享感兴趣,欢迎加 ...