引言

在C#的并发编程中,Channel是一种非常强大的数据结构,用于在生产者和消费者之间进行通信。本文将首先通过一个实际的使用案例,介绍如何在C#中使用Channel,然后深入到Channel的源码中,解析其内部的实现机制。

使用案例一:文件遍历和过滤

在我们的使用案例中,我们需要遍历一个文件夹及其所有子文件夹,并过滤出具有特定扩展名的文件。在此,我们使用了C#的Channel来实现这个任务。

首先,我们创建了一个名为EnumerateFilesRecursively的方法,这个方法接受一个文件夹路径作为参数,并返回一个ChannelReader。这个方法中,我们创建了一个有界的Channel,然后在一个单独的任务中遍历指定的文件夹及其所有子文件夹,并将找到的每个文件的路径写入Channel。当遍历完成后,我们关闭Channel的写入端。

ChannelReader<string> EnumerateFilesRecursively(string root, int capacity = 100, CancellationToken token = default)
{
var output = Channel.CreateBounded<string>(capacity); async Task WalkDir(string path)
{
IEnumerable<string> files = null, directories = null;
try
{
files = Directory.EnumerateFiles(path);
directories = Directory.EnumerateDirectories(path);
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
} if (files != null)
{
foreach (var file in files)
{
await output.Writer.WriteAsync(file, token);
}
} if (directories != null)
await Task.WhenAll(directories.Select(WalkDir));
} Task.Run(async () =>
{
await WalkDir(root);
output.Writer.Complete();
}, token); return output.Reader;
}

然后,我们创建了一个名为FilterByExtension的方法,这个方法接受一个ChannelReader和一个扩展名集合作为参数,并返回一个ChannelReader。在这个方法中,我们创建了一个无界的Channel,然后在一个单独的任务中从输入的Channel中读取每个文件路径,检查其扩展名,如果满足条件,就将其转换为FileInfo并写入输出的Channel。当所有的文件都被处理后,我们关闭Channel的写入端。

ChannelReader<FileInfo> FilterByExtension(
ChannelReader<string> input, IReadOnlySet<string> exts, CancellationToken token = default)
{
var output = Channel.CreateUnbounded<FileInfo>(); Task.Run(async () =>
{
try
{
await foreach (var file in input.ReadAllAsync(token).ConfigureAwait(false))
{
var fileInfo = new FileInfo(file);
if (exts.Contains(fileInfo.Extension))
await output.Writer.WriteAsync(fileInfo, token).ConfigureAwait(false);
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
finally
{
output.Writer.Complete();
}
}, token); return output;
}

最后,在Main方法中,我们首先调用EnumerateFilesRecursively方法,遍历指定的文件夹,并得到一个文件路径的Channel。然后,调用FilterByExtension方法,过滤出具有特定扩展名的文件,并得到一个文件信息的Channel。最后,遍历这个Channel,打印出每个文件的全路径。

var fileSource = EnumerateFilesRecursively("D:\\Program Files\\.nuget\\packages");
var sourceCodeFiles =
FilterByExtension(fileSource, new HashSet<string> { ".json", ".map", ".dll" }); await foreach (var file in sourceCodeFiles.ReadAllAsync().ConfigureAwait(false))
{
Console.WriteLine($"{file.FullName}");
} Console.ReadKey();

在这个例子中,可以看到无论是文件的遍历还是过滤,都是并行进行的,并且这两个任务之间通过Channel进行了解耦,使得代码更加简洁和清晰。此外,由于Channel的异步特性,我们的程序在等待数据的时候不会阻塞,从而大大提高了程序的性能和响应性。

使用案例二:Excel读取与翻译内容

在我们的使用案例中,我们需要读取Excel文件,同时将读取的内容处理,调用对应的翻译服务进行翻译,并将翻译结果打印到控制台并存储到新的Excel文件中。为此,我们定义了一个名为ExcelTranslationProvider的类。

ExcelTranslationProvider类

ExcelTranslationProvider类是一个专门处理Excel文件翻译的工具。它主要使用了.NET的Channel来处理异步数据流,从而提高了翻译的效率。以下是该类的代码:

public class ExcelTranslationProvider : TranslationProvider
{
public static Translater Translater { get; set; } = Translater.Azure; public static II18NTermTranslateService TranslateService => TranslateServiceProvider.GetTranslateService(Translater); private static ExcelTranslationParameters translationParameters;
public static async Task Translate(TranslationParameters parameters)
{
if (parameters is not ExcelTranslationParameters excelParameters)
throw new ArgumentException("Invalid parameters for Excel translation.");
translationParameters = excelParameters; var translateText = TranslateText(excelParameters.Path);
var i = 1;
List<TranslationDto> list = new List<TranslationDto>();
await foreach (var text in translateText.ReadAllAsync().ConfigureAwait(false))
{
System.Console.WriteLine($"{i++}、" + text.TranslatText);
list.Add(text);
}
await ExcelUtil.SaveAsAsync(excelParameters.SavePath, list);
} private static ChannelReader<TranslationDto> TranslateText(string path)
{
var output = Channel.CreateUnbounded<TranslationDto>(); _ = TranslateAndWriteToChannelAsync(path, output.Writer); return output.Reader;
} private static async Task TranslateAndWriteToChannelAsync(string path, ChannelWriter<TranslationDto> writer)
{
var query = await ExcelUtil.QueryAsync<TranslationDto>(path, translationParameters.Sheet);
var tasks = query.Select(async item =>
{
try
{
var res = await TranslateService.TranslateSync(item.Name, "en-US");
item.TranslatText = res;
await writer.WriteAsync(item);
}
catch (Exception ex)
{
System.Console.WriteLine($"An error occurred: {ex.Message}");
}
}); await Task.WhenAll(tasks);
writer.Complete();
}
}
  • Translater和TranslateService:这两个静态属性用于配置和获取翻译服务。Translater是一个枚举类型,表示可用的翻译服务提供者。默认的翻译服务是Azure。TranslateService是一个只读属性,返回一个实现了II18NTermTranslateService接口的翻译服务对象。这个对象是通过TranslateServiceProvider.GetTranslateService(Translater)方法获取的。

  • translationParameters:用于保存翻译参数,这些参数包括源文件的路径、目标文件的路径等。

  • Translate:这个方法首先检查传入的参数是否为ExcelTranslationParameters类型。然后,它调用TranslateText方法开始翻译过程。翻译的结果被保存在一个List列表中,然后写入到Excel文件。

  • TranslateText:它创建了一个无界Channel,并启动了一个异步任务来进行翻译操作并将结果写入到Channel中。无界Channel是一种可以存储任意数量元素的Channel,它是通过Channel.CreateUnbounded()方法创建的。创建Channel后,这个方法返回Channel的读取端,同时启动了一个异步任务TranslateAndWriteToChannelAsync来进行翻译并将结果写入到Channel的中。

  • TranslateAndWriteToChannelAsync:它负责从Excel文件中读取数据,进行翻译,并将翻译结果写入到Channel中。这个方法首先从Excel文件中读取数据,然后为每一条数据创建一个异步翻译任务。所有的翻译任务是并发执行的,使用了Task.WhenAll(tasks)来等待所有的翻译任务完成。完成所有的翻译任务后,这个方法调用writer.Complete()方法来表示没有更多的数据要写入到Channel中。

理解并掌握C#的Channel:从使用案例到源码解读(一)的更多相关文章

  1. 简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析

    简单理解 OAuth 2.0 及资料收集,IdentityServer4 部分源码解析 虽然经常用 OAuth 2.0,但是原理却不曾了解,印象里觉得很简单,请求跳来跳去,今天看完相关介绍,就来捋一捋 ...

  2. WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码

    上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们 ...

  3. 深入理解JAVA集合系列一:HashMap源码解读

    初认HashMap 基于哈希表(即散列表)的Map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和null键. HashMap继承于AbstractMap,实现了Map.Cloneab ...

  4. 书籍《深入理解Spring Cloud 与微服务构建》勘误、源码下载

    转载请标明出处: https://blog.csdn.net/forezp/article/details/79638403 本文出自方志朋的博客 文章勘误 错误在所难免,欢迎大家批评指正,在文章下方 ...

  5. vue系列---理解Vue中的computed,watch,methods的区别及源码实现(六)

    _ 阅读目录 一. 理解Vue中的computed用法 二:computed 和 methods的区别? 三:Vue中的watch的用法 四:computed的基本原理及源码实现 回到顶部 一. 理解 ...

  6. hashMap 源码解读理解实现原理和hash冲突

    hashMap 怎么说呢. 我的理解是 外表是一个set 数组,无序不重复 . 每个set元素是一个bean ,存着一对key value 看看代码吧 package test; import jav ...

  7. go语言nsq源码解读九 tcp和http中channel、topic的增删

    通过前面多篇文章,nsqlookupd基本已经解读完毕了,不过在关于channel和topic的增删上还比较模糊,所以本篇将站在宏观的角度来总结一下,tcp.go和http.go两个文件中关于chan ...

  8. JUC回顾之-ConcurrentHashMap源码解读及原理理解

    ConcurrentHashMap结构图如下: ConcurrentHashMap实现类图如下: segment的结构图如下: package concurrentMy.juc_collections ...

  9. 深入理解JAVA集合系列二:ConcurrentHashMap源码解读

    HashMap和Hashtable的区别 在正式开始这篇文章的主题之前,我们先来比较下HashMap和Hashtable之间的差异点: 1.Hashtable是线程安全的,它对外提供的所有方法都是都使 ...

  10. React 源码解读参考,理解原理。

    Rubix - ReactJS Powered Admin Template 文档:   http://rubix-docs.sketchpixy.com/ ===================== ...

随机推荐

  1. 2023-06-05:Redis官方为什么不提供 Windows版本?

    2023-06-05:Redis官方为什么不提供 Windows版本? 答案2023-06-05: Redis官方没有提供Windows版本有几个原因. 1.Redis的开发团队规模较小,由三四名核心 ...

  2. 【Linux】shell编程(一) 变量

    [Linux]shell编程(一) 变量 目录 [Linux]shell编程(一) 变量 什么是shell编程 如何运行shell脚本 第一行 #!/bin/bash 第一行叫什么? WHAT IS ...

  3. 记一次BootCDN被黑产挂马导致站点跳转博彩网站的问题

    近期发现公司某些站点出现偶尔跳转博彩网站的现象,经过排查发现该现象为供应链投毒攻击,BootCDN上的静态资源无一例外均被污染, 当外站引入BootCDN的静态资源时,如果请求携带的Referer头为 ...

  4. GoldenEye项目实战

    前言 "操千曲而后晓声,观千剑而后识器",下载靶机项目实战提升自我,这是一个涉及到渗透与CTF联合的实战项目. Descript: 我最近完成了一个OSCP类型的易受攻击机器的创建 ...

  5. docker部署springboot项目到服务器

    docker部署springboot demo到vps docker安装 首先检查docker是否安装 docker version 出现上述界面就是安装成功 如果没有安装docker的话,运行以下命 ...

  6. 【HarmonyOS】如何获取公共目录的图片等文件(API7 FA模型JS语言)

    ​ [关键字] API7.JS.公共目录.@ohos.multimedia.mediaLibrary [前言] 在使用API7 JS开发HarmonyOS应用时,需要获取非应用目录即内部存储公共目录下 ...

  7. 每日一题 力扣 1377 https://leetcode.cn/problems/frog-position-after-t-seconds/

    力扣 1377 https://leetcode.cn/problems/frog-position-after-t-seconds/ 这道题目用dp去做,构建邻接矩阵,做的时候需要注意题目条件,如果 ...

  8. LaTeX 的学习笔记

    摘自我的洛谷博客 该文章被打开的次数(包括洛谷平台): \(\LaTeX\) 中所有命令都以\开头,后面可以跟一个花括号,代表参数. \documentclass{} 指定了文章类型,有 articl ...

  9. .net 温故知新【12】:Asp.Net Core WebAPI 中的Rest风格

    RPC RPC(Remote Procedure Call),远程过程调用),这种RPC形式的API组织形态是类和方法的形式.所以API的请求往往是一个动词用来标识接口的意思,比如 https://x ...

  10. 2023年icpc大学生程序设计竞赛-crf

    第一次在除郑轻以外的校外的地方比赛,也是第一次出市比赛,赛程也比较长.20号出发的时候遇到一些意外,不过无伤大雅,第一天热身赛平平无奇,晚上的时候补了一下前年icpc的题,一个多小时做了五题,很是自信 ...