经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
理解并掌握C#的Channel:从使用案例到源码解读(一)
来源:cnblogs  作者:HueiFeng  时间:2023/9/25 16:50:08  对本文有异议

引言

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

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

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

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

  1. ChannelReader<string> EnumerateFilesRecursively(string root, int capacity = 100, CancellationToken token = default)
  2. {
  3. var output = Channel.CreateBounded<string>(capacity);
  4. async Task WalkDir(string path)
  5. {
  6. IEnumerable<string> files = null, directories = null;
  7. try
  8. {
  9. files = Directory.EnumerateFiles(path);
  10. directories = Directory.EnumerateDirectories(path);
  11. }
  12. catch (Exception ex)
  13. {
  14. Console.WriteLine($"An error occurred: {ex.Message}");
  15. }
  16. if (files != null)
  17. {
  18. foreach (var file in files)
  19. {
  20. await output.Writer.WriteAsync(file, token);
  21. }
  22. }
  23. if (directories != null)
  24. await Task.WhenAll(directories.Select(WalkDir));
  25. }
  26. Task.Run(async () =>
  27. {
  28. await WalkDir(root);
  29. output.Writer.Complete();
  30. }, token);
  31. return output.Reader;
  32. }

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

  1. ChannelReader<FileInfo> FilterByExtension(
  2. ChannelReader<string> input, IReadOnlySet<string> exts, CancellationToken token = default)
  3. {
  4. var output = Channel.CreateUnbounded<FileInfo>();
  5. Task.Run(async () =>
  6. {
  7. try
  8. {
  9. await foreach (var file in input.ReadAllAsync(token).ConfigureAwait(false))
  10. {
  11. var fileInfo = new FileInfo(file);
  12. if (exts.Contains(fileInfo.Extension))
  13. await output.Writer.WriteAsync(fileInfo, token).ConfigureAwait(false);
  14. }
  15. }
  16. catch (Exception ex)
  17. {
  18. Console.WriteLine($"An error occurred: {ex.Message}");
  19. }
  20. finally
  21. {
  22. output.Writer.Complete();
  23. }
  24. }, token);
  25. return output;
  26. }

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

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

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

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

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

ExcelTranslationProvider类

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

  1. public class ExcelTranslationProvider : TranslationProvider
  2. {
  3. public static Translater Translater { get; set; } = Translater.Azure;
  4. public static II18NTermTranslateService TranslateService => TranslateServiceProvider.GetTranslateService(Translater);
  5. private static ExcelTranslationParameters translationParameters;
  6. public static async Task Translate(TranslationParameters parameters)
  7. {
  8. if (parameters is not ExcelTranslationParameters excelParameters)
  9. throw new ArgumentException("Invalid parameters for Excel translation.");
  10. translationParameters = excelParameters;
  11. var translateText = TranslateText(excelParameters.Path);
  12. var i = 1;
  13. List<TranslationDto> list = new List<TranslationDto>();
  14. await foreach (var text in translateText.ReadAllAsync().ConfigureAwait(false))
  15. {
  16. System.Console.WriteLine($"{i++}、" + text.TranslatText);
  17. list.Add(text);
  18. }
  19. await ExcelUtil.SaveAsAsync(excelParameters.SavePath, list);
  20. }
  21. private static ChannelReader<TranslationDto> TranslateText(string path)
  22. {
  23. var output = Channel.CreateUnbounded<TranslationDto>();
  24. _ = TranslateAndWriteToChannelAsync(path, output.Writer);
  25. return output.Reader;
  26. }
  27. private static async Task TranslateAndWriteToChannelAsync(string path, ChannelWriter<TranslationDto> writer)
  28. {
  29. var query = await ExcelUtil.QueryAsync<TranslationDto>(path, translationParameters.Sheet);
  30. var tasks = query.Select(async item =>
  31. {
  32. try
  33. {
  34. var res = await TranslateService.TranslateSync(item.Name, "en-US");
  35. item.TranslatText = res;
  36. await writer.WriteAsync(item);
  37. }
  38. catch (Exception ex)
  39. {
  40. System.Console.WriteLine($"An error occurred: {ex.Message}");
  41. }
  42. });
  43. await Task.WhenAll(tasks);
  44. writer.Complete();
  45. }
  46. }
  • 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.cnblogs.com/yyfh/p/mastering-csharp-channels-part-one.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号