理解并掌握C#的Channel:从使用案例到源码解读(一)

这篇具有很好参考价值的文章主要介绍了理解并掌握C#的Channel:从使用案例到源码解读(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在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中。文章来源地址https://www.toymoban.com/news/detail-712104.html

到了这里,关于理解并掌握C#的Channel:从使用案例到源码解读(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++初阶之一篇文章让你掌握vector(理解和使用)

    在C++中,std::vector是标准模板库(STL)中的一种动态数组容器,它可以存储任意类型的元素,并且能够自动调整大小。std::vector提供了许多方便的成员函数,使得对数组的操作更加简单和高效。 vector声明 : template class T, class Alloc = allocatorT ; 这是 std::vector 的一般模板定义。它

    2024年02月14日
    浏览(33)
  • 掌握 C# 变量:在代码中声明、初始化和使用不同类型的综合指南

    变量是用于存储数据值的容器。 在 C# 中,有不同类型的变量(用不同的定义),例如: int - 存储整数(没有小数点的整数),如 123 或 -123 double - 存储浮点数,有小数点,如 19.99 或 -19.99 char - 存储单个字符,如 \\\'a\\\' 或 \\\'B\\\'。 Char 值用单引号括起来 string - 存储文本,如

    2024年01月17日
    浏览(38)
  • channel 源码解析(5问)

    目录 1.channel底层数据结构是什么 2.channel创建的底层实现 3.channel 的发送过程 4.channel的接受过程 5.关闭 channel channel底层的数据结构是hchan,包括一个循环链表和2个双向链表 sendx:循环链表中已发送的位置 recvx: 循环链表中已接受的位置 qcount:环形队列的实际大小 dataqsiz:环形队

    2023年04月13日
    浏览(19)
  • C#程序设计——Windows应用程序开发,1、初步掌握Windows应用程序的设计方法。2、掌握常用窗体控件的使用方法。

    初步掌握Windows应用程序的设计方法。 掌握常用窗体控件的使用方法。 1、设计一个Windows应用程序,创建一个用于添加学生个人基本信息的窗体,窗体下方法同时滚动信息“天行健,君子以自强不息!”。   要示如下: 如图1所示,设计窗体界面控件的布局 图1 学生信息管理

    2024年02月10日
    浏览(60)
  • CountDownLatch倒计时器源码解读与使用

    🏷️ 个人主页 :牵着猫散步的鼠鼠  🏷️ 系列专栏 :Java全栈-专栏 🏷️ 个人学习笔记,若有缺误,欢迎评论区指正   目录 1. 前言 2. CountDownLatch有什么用 3. CountDownLatch底层原理 3.1. countDown()方法 3.2. await()方法 4. CountDownLatch的基本使用 5. 总结 在很多的面经中都看到过提问

    2024年04月22日
    浏览(23)
  • Flask_Login使用与源码解读

    用户登录后,验证状态需要记录在会话中,这样浏览不同页面时才能记住这个状态,Flask_Login是Flask的扩展,专门用于管理用户身份验证系统中的验证状态。 注:Flask是一个微框架,仅提供包含基本服务的核心(路由、网关接口、模板),其它功能都是通过扩展来实现,它有无

    2024年02月06日
    浏览(28)
  • javaScript:分支语句的理解与使用(附带案例)

    目录 前言 补充 另一种说法  分支语句 1.if语句  a.单分支语句 注意  b.双分支语句  注意点  c.多分支语句(分支语句的联级语句)  补充   2.三元运算符 三元运算符 ? : 使用场景 3.switch语句 解释 释义: 注意事项: 相关代码  案例  1.在input框输入两个数字判断大小 效

    2024年02月13日
    浏览(31)
  • c# MathNet.Numerics 圆拟合使用案例

    下载地址 https://www.nuget.org/packages/MathNet.Numerics/4.15.0#supportedframeworks-body-tab 原理 从标准圆方程(x-c1) 2+(y-c2) 2=r 2中进行方程变换得到2xc1+2yc2+(r 2−c1 2−c2 2)=x 2+y 2,其中,我们c3替换常量值r 2−c1 2−c2 2,即:r 2−c1 2−c2 2=c3。由此,我们得到2xc1+2yc2+c3=x 2+y 2,将点集带入,方程就

    2024年01月24日
    浏览(26)
  • Netty源码学习3——Channel ,ChannelHandler,ChannelPipeline

    系列文章目录和关于我 在Netty源码学习2——NioEventLoop的执行中,我们学习了NioEventLoop是如何进行事件循环以及如何修复NIO 空轮询的bug的,但是没有深入了解IO事件在netty中是如何被处理的,下面我们以服务端demo代码为例子,看下和IO事件处理密切的Channel 如上在编写netty 服务

    2024年02月11日
    浏览(28)
  • unity C#什么是线程锁,以及使用案例

    线程锁的原理主要是为了在多线程环境下提供对共享资源访问的同步机制,防止多个线程同时读写同一数据导致的数据不一致、竞态条件和死锁等问题。以下是线程锁基本工作原理的概述: 1. 互斥 (Mutual Exclusion): 线程锁的核心原理是互斥,即在同一时刻,只有一个线程能

    2024年01月22日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包