在 .NET Core 中使用 ActionBlock 实现高效率的多步骤数据处理
一、引言
上一篇博客 分享了使用 Channel 来实现针对大量数据的多线程异步处理,感谢大哥们在评论中提出的宝贵的问题和建议!本篇将分享使用 ActionBlock 如何实现,欢迎在评论区留言讨论。
二、ActionBlock介绍
什么是 ActionBlock?
ActionBlock是 .NET 中 TPL Dataflow 库的一部分,用于处理数据流和并行任务。它提供了一种简单而强大的方式来处理并行任务,并且可以轻松地实现生产者-消费者模式。
ActionBlock 的特点
- 并行处理:
ActionBlock可以配置为并行处理多个任务,从而提高处理效率 - 异步编程:支持异步编程模型,可以避免阻塞线程,提高应用程序的响应速度和吞吐量
- 数据流控制:可以通过设置最大并行度和其他选项来控制数据流的处理方式
- 任务调度:可以用于调度和管理并行任务,确保任务按预期执行
ActionBlock 的使用场景
- 生产者-消费者模式:可以用于实现生产者-消费者模式,其中生产者将数据发送到
ActionBlock,消费者从ActionBlock中读取数据并进行处理 - 数据流处理:适用于需要处理大量数据并且需要并行处理的场景,例如日志处理、数据转换等
- 任务调度:可以用于调度和管理并行任务,确保任务按预期执行
ActionBlock 的基本用法
使用ActionBlock非常简单,主要步骤如下:
- 创建 ActionBlock:定义一个 ActionBlock,指定要执行的操作和并行选项
- 发送数据到 ActionBlock:使用
SendAsync方法将数据发送到 ActionBlock - 完成 ActionBlock:在所有数据发送完成后,调用
Complete方法通知 ActionBlock 不再接收新的数据 - 等待处理完成:使用
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完全处理完毕。
四、解决方案
使用TransformBlock和ActionBlock来实现生产者-消费者模式。生产者负责读取数据并将其发送到TransformBlock中,消费者从TransformBlock中读取数据并进行处理。
以下是一个简单的示例代码,演示如何使用TransformBlock和ActionBlock实现生产者-消费者模式来处理数据:
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}");
}
}
代码解释:
- 创建Step1的 TransformBlock:在
ProcessAsync方法中,我们首先创建了一个 TransformBlock,用于Step1的处理,TransformBlock 接受一个输入数据项,进行处理后返回一个输出数据项,TransformBlock<string, string>表示输入和输出都是string类型 - 创建Step2的 ActionBlock:创建一个 ActionBlock 用于Step2的处理,ActionBlock 接受一个输入数据项并进行处理,但不返回输出数据项。
ActionBlock<string>表示输入是string类型 - 链接 TransformBlock 和 ActionBlock:将 TransformBlock 链接到 ActionBlock ,以便将Step1的处理结果发送到Step2进行处理,使用
LinkTo方法将两个块连接起来,并设置PropagateCompletion为 true,表示当 TransformBlock 完成时,ActionBlock 也会完成 - 启动Step1的任务:逐个将数据项发送到 TransformBlock,并等待所有数据处理完成,使用
SendAsync方法将数据项发送到 TransformBlock - 等待任务完成:使用
Complete方法通知 TransformBlock 不再接收新的数据,并使用Completion属性等待所有数据处理完成。然后完成Step2的 ActionBlock 的写入,并等待Step2的 ActionBlock 处理完成
在 .NET Core 中使用 ActionBlock 实现高效率的多步骤数据处理的更多相关文章
- 在ef core中使用postgres数据库的全文检索功能实战之中文支持
前言 有关通用的postgres数据库全文检索在ef core中的使用方法,参见我的上一篇文章. 本文实践了zhparser中文插件进行全文检索. 准备工作 安装插件,最方便的方法是直接使用安装好插件 ...
- .NET Core 中的日志与分布式链路追踪
目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...
- .net core中Grpc使用报错:The remote certificate is invalid according to the validation procedure.
因为Grpc采用HTTP/2作为通信协议,默认采用LTS/SSL加密方式传输,比如使用.net core启动一个服务端(被调用方)时: public static IHostBuilder Creat ...
- .NET Core中的认证管理解析
.NET Core中的认证管理解析 0x00 问题来源 在新建.NET Core的Web项目时选择“使用个人用户账户”就可以创建一个带有用户和权限管理的项目,已经准备好了用户注册.登录等很多页面,也可 ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- Asp.net Core中使用Session
前言 2017年就这么悄无声息的开始了,2017年对我来说又是特别重要的一年. 元旦放假在家写了个Asp.net Core验证码登录, 做demo的过程中遇到两个小问题,第一是在Asp.net Cor ...
- 在ASP.NET Core中使用百度在线编辑器UEditor
在ASP.NET Core中使用百度在线编辑器UEditor 0x00 起因 最近需要一个在线编辑器,之前听人说过百度的UEditor不错,去官网下了一个.不过服务端只有ASP.NET版的,如果是为了 ...
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
随机推荐
- .NET高级调试 - 3.7对象检查
简介 在大多数调试会话中,首先需要检查的项目就是分析应用程序的状态.在确认程序的问题是某种无效状态造成的,我们便需要分析程序是如何变成无效状态的.那么在分析过程中,需要为我们深入了解对象的各种审查方法 ...
- 程序员开发利器:Your Commands网站上线
程序员开发利器:Your Commands网站上线 先上链接: https://www.ycmds.cc 背景 各种命令行工具是我们IT行业日常工作离不开的,但是对于命令行工具的使用有一个痛点:文档上 ...
- 什么是WEB3.0
WEB1.0 90年代末期创建的搜狐,新浪等门户网站的特点是向用户推送信息,门户网站显示什么我们看什么,这个时代称为web1.0.WEB2.0随着技术的发展,用户可以在网上进行互动,可以在网站上发表个 ...
- ROS入门21讲(5)
九.服务数据的定义与使用 1.服务模型 2.自定义服务数据 Person.srv string name uint8 sex uint8 age uint8 unknown = 0 uint8 mal ...
- 不敢相信,Nginx 还能这么玩?
大家好,我是程序员鱼皮.今天来聊聊 Nginx 技术,这是一个企业项目必用,但是却经常被程序员忽略的技术.学好 Nginx,可以助你在求职中脱颖而出. 或许你会想:"Nginx 不就是用来部 ...
- 内网 BitTorrent 下载环境搭建——基于 Transmission
背景 前段时间为公司的产品增加了磁力链.种子下载的能力,测试时发现网上搜到的热门种子,有时好用,有时不好用,不好用主要表现在:没速度.速度慢.速度不稳定.下载一部分后没速度等,严重拖累了测试工作.为此 ...
- CF1019C Sergey's problem
CF1019C Sergey's problem 很巧妙的构造题. 思路 首先我们可以把这题分成两个部分: 解决覆盖问题 解决边冲突问题 \(vis_i\) 为 \(i\) 点是否被覆盖的标记,\(c ...
- 【一步步开发AI运动小程序】十九、运动识别中如何解析RGBA帧图片?
引言 最近有不少开发者向我们咨询,像体测.赛事等应用场景中,需要保存运动过程的图像,如何将相机抽取的RGBA帧图像解析成.jpg或.png格式的图像?今天我们就为您介绍相应的解决方案. 一.RGBA图 ...
- mouseup模拟drag与click事件冲突
今天要说一个很隐晦的东西,一般可能很难碰到,碰到了可能很难解决.就是当我们自己用mousestart,mousemove,mouseup做自定义拖拽效果的时候,如果这个时候配上click就会引发一个拖 ...
- golang 判断文件或文件夹是否存在
//判断文件是否存在 存在返回 true 不存在返回false func checkFileIsExist(filename string) bool { var exist = true if _, ...