一、引言

上一篇博客 分享了使用 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. 2022年8月中国数据库排行榜:openGauss重夺榜眼,PolarDB反超人大金仓

    "烈日杲杲,夺榜愈烈." 2022年8月的 墨天轮中国数据库流行度排行榜 火热出炉,8月排行榜共有236个数据库参与排名.本月榜单前十名的变化可以用"两反超"来 ...

  2. CSP-S 2022~2023 补题

    下面的代码都是远古代码,不打算重写了. CSP-S 2023 T1 密码锁 题意:一个密码锁上有 \(5\) 个位置,每个位置上的数字是 \(0 \sim 9\) 的循环,每次打乱会选择一或两个相邻位 ...

  3. FFmpeg开发笔记(五十七)使用Media3的Transformer加工视频文件

    ​继音视频播放器ExoPlayer之后,谷歌又推出了音视频转换器Transformer,要在音视频加工领域施展拳脚.根据Android开发者官网介绍:Jetpack Media3是Android媒体库 ...

  4. 中国移动基于 Kubernetes 的物联网边缘计算应用实践

    作者:何毓川,中移物联网,云计算开发高级工程师 EdgeBox简介 中移物联网是中国移动集团在物联网方向的专业研发子公司,在各个垂直行业都有非常丰富和完成的解决方案. 本文通过中移物联网的物联网边缘计 ...

  5. 云原生爱好者周刊:KubeSphere 3.3.0 Alpha 版发布

    开源项目推荐 Collection of Linux eBPF slides/documents 这个项目收集了各种与 eBPF 相关的资料,非常全面. magic-trace magic-trace ...

  6. flink jobmanager 终止任务失败问题

    flink jobmanager 终止任务失败问题 :root { --default-font: ui-sans-serif, -apple-system, BlinkMacSystemFont, ...

  7. Java受保护的访问修饰符protected

    声明:我不去上来给大家那一张被扯来扯去的√,×表,什么表想必大家心里清楚 下面进入正题: protected 的主要着眼点在于两点: 1.子类继承 2.是否同包 心里记着上面的那两点,那么protec ...

  8. sort()的函数参数 —— function(a,b){return a -b} 相关问题_20220114

    sort()的函数参数--function(a,b){return a -b} 相关问题 sort()的定义和用法 sort() 方法用于对数组的元素进行排序. 排序顺序可以是字母或数字,并按升序或降 ...

  9. C# 删除Word文档中的段落

    在编辑Word文档时,我们有时需要调整段落的布局.删除不必要的段落以优化文档的结构和阅读体验.本文将通过以下3个简单示例演示如何使用免费.NET库删除Word文档中的段落 . C# 删除Word中的指 ...

  10. 3.19 Linux命令的执行过程是怎样的?(新手必读)

    前面讲过,在 Linux 系统中"一切皆文件",Linux 命令也不例外.那么,当编辑完成 Linux 命令并回车后,系统底层到底发生了什么事情呢? 简单来说,Linux 命令的执 ...