.NET 中 Channel 类简单使用

net,channel · 浏览次数 : 0

小编点评

**Channel 类介绍** `Channel` 类是 .NET Standard 中提供的一种同步数据结构,用于在生产者和消费者之间异步传递数据。该类基于生产者/消费者模式的 conceptual programming,允许您创建一个无界或有界的通道,并通过异步操作将数据传递给消费者。 **主要方法:** * `CreateUnbounded`:创建一个无界的通道,并默认情况下使用缓存行为。 * `CreateBounded(int capacity)`:创建一个有界的通道,并指定通道的容量。 * `Writer.WriteAsync()`:写入数据到通道的写入器中。 * `Reader.ReadAllAsync()`:从通道中读取所有数据。 * `Complete()`:通知通道所有生产者停止写入数据。 **示例:** ```csharp // 创建无界的通道 var channel = Channel.CreateUnbounded(); // 创建生产者和消费者 var producer = Task.Run(async () => { // 写数据到通道 await channel.Writer.WriteAsync(1); await channel.Writer.WriteAsync(2); }); var consumer = Task.Run(async () => { // 从通道中读取数据 foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine(item); } }); // 等待所有线程完成 await Task.WhenAll(producer, consumer); ``` **主要用途:** * 高并发应用程序 * 容错处理 * 消息队列 * 进程间通信 **性能:** Channel 类提供了非常高的性能,因为它使用缓存行为和异步操作。对于需要快速数据传输的应用程序来说,这是非常重要的。

正文

Channel 是干什么的

The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consumers asynchronously. The library targets .NET Standard and works on all .NET implementations.
Channels are an implementation of the producer/consumer conceptual programming model.
以上是微软官方的解释 channels。用中文说的话就是这个类提供了在生产者跟消费者之间异步传统数据的能力,简单来说可以认为是一个内存消息队列。

示例 1

下面是一个简单的示例,说明如何使用 Channel 类来创建一个生产者-消费者模型:

    static async Task Main(string[] args)
    {
        var channel = Channel.CreateUnbounded<int>();

        var producer = Task.Run(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await channel.Writer.WriteAsync(i);
                await Task.Delay(1000); // 模拟生产者需要一些时间来生成数据
            }

            channel.Writer.Complete();
        });

        var consumer = Task.Run(async () =>
        {
            await foreach (var item in channel.Reader.ReadAllAsync())
            {
                Console.WriteLine($"消费者接收到: {item}");
            }
        });

        await Task.WhenAll(producer, consumer);
    }

在这个例子中,我们创建了一个无界的通道,然后创建了两个任务,一个是生产者,一个是消费者。生产者每秒生成一个数字,然后写入通道。消费者从通道中读取数据并打印出来。当生产者完成写入后,它会调用 channel.Writer.Complete() 来通知消费者没有更多的数据可以读取。

示例 2

你可以使用 Channel.CreateBounded(capacity) 方法来创建一个有界的通道,其中 capacity 参数指定了通道的容量。当通道满时,尝试写入的操作将会阻塞,直到有空间可用。

    static async Task Main(string[] args)
    {
        var channel = Channel.CreateBounded<int>(5); // 创建一个容量为5的有界通道

        var producer = Task.Run(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await channel.Writer.WriteAsync(i);
                Console.WriteLine($"生产者生成了: {i}");
                await Task.Delay(1000); // 模拟生产者需要一些时间来生成数据
            }

            channel.Writer.Complete();
        });

        var consumer = Task.Run(async () =>
        {
            await foreach (var item in channel.Reader.ReadAllAsync())
            {
                Console.WriteLine($"消费者接收到: {item}");
                await Task.Delay(2000); // 模拟消费者需要一些时间来处理数据
            }
        });

        await Task.WhenAll(producer, consumer);
    }

在这个例子中,我们创建了一个容量为5的有界通道。生产者每秒生成一个数字,然后写入通道。消费者从通道中读取数据并打印出来,但消费者处理数据的速度比生产者慢,所以当通道满时,生产者的 WriteAsync 操作将会阻塞,直到消费者读取了一些数据,使得通道有空间可用。

示例 3

下面是一个示例,展示了如何在多个生产者和消费者之间共享一个通道:

    static async Task Main(string[] args)
    {
        var channel = Channel.CreateUnbounded<int>();

        // 创建两个生产者
        var producer1 = Produce(channel.Writer, id: 1);
        var producer2 = Produce(channel.Writer, id: 2);

        // 创建两个消费者
        var consumer1 = Consume(channel.Reader, id: 1);
        var consumer2 = Consume(channel.Reader, id: 2);

        // 等待所有生产者和消费者完成
        await Task.WhenAll(producer1, producer2, consumer1, consumer2);
    }

    static async Task Produce(ChannelWriter<int> writer, int id)
    {
        for (int i = 0; i < 10; i++)
        {
            await writer.WriteAsync(i);
            Console.WriteLine($"生产者{id}生成了: {i}");
            await Task.Delay(1000); // 模拟生产者需要一些时间来生成数据
        }

        writer.Complete();
    }

    static async Task Consume(ChannelReader<int> reader, int id)
    {
        await foreach (var item in reader.ReadAllAsync())
        {
            Console.WriteLine($"消费者{id}接收到: {item}");
            await Task.Delay(2000); // 模拟消费者需要一些时间来处理数据
        }
    }

在这个例子中,我们创建了两个生产者和两个消费者,它们都共享同一个通道。这是一个非常重要使用模式。因为当我们使用消息队列的时候往往会有多个生产者跟多个消费者。我们可以通过控制生产者生产的速度来控制推入队列的数据量。我们还可以通过控制消费者的数量来控制消费数据的速度,从而来调节系统的流量,达到消峰填谷的作用。

总结

Channel 类是 .NET CORE 3.0 后新加入的类。为我们提供了便利的生产者/消费者模式实现方案。相当于是一个进程内的内存队列,而且它没有持久化,纯内存操作,性能是非常非常高的。当我们面对真正的高并发的时候可以为我们的系统提供吞吐量。当然代价是内存跟放弃一些实时性。

关注我的公众号一起玩转技术

与.NET 中 Channel 类简单使用相似的内容:

.NET 中 Channel 类简单使用

Channel 是干什么的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume

C#.Net筑基-集合知识全解

.Net 中提供了一系列的管理对象集合的类型,数组、可变列表、字典等。从类型安全上集合分为两类,泛型集合 和 非泛型集合,传统的非泛型集合存储为Object,需要类型转。而泛型集合提供了更好的性能、编译时类型安全,推荐使用。

零基础写框架(3): Serilog.NET 中的日志使用技巧

.NET 中的日志使用技巧 Serilog Serilog 是 .NET 社区中使用最广泛的日志框架,所以笔者使用一个小节单独讲解使用方法。 示例项目在 Demo2.Console 中。 创建一个控制台程序,引入两个包: Serilog.Sinks.Console Serilog.Sinks.Fil

.NET 中使用 OpenTelemetry Traces 追踪应用程序

上一次我们讲了 OpenTelemetry Logs。今天继续来说说 OpenTelemetry Traces。 在今天的微服务和云原生环境中,理解和监控系统的行为变得越来越重要。在当下我们实现一个功能可能需要调用了 N 个方法,涉及到 N 个服务。方法之间的调用如蜘蛛网一样。分布式追踪这个时候就至

.NET 中的表达式树

.NET 中的表达式树(Expression Trees) 表达式树是什么? 表达式树(Expression Trees)是.NET框架中的一个强大功能,它将代码表示为一个由表达式节点组成的树形结构。每个节点代表代码中的一个操作,例如方法调用、算术运算、逻辑运算等。表达式树允许开发者在运行时分析、修

.NET中委托性能的演变

## .NET中的委托 .NET中的委托是一项重要功能,可以实现间接方法调用和函数式编程。 自.NET Framework 1.0起,委托在.NET中就支持多播(multicast)功能。通过多播,我们可以在单个委托调用中调用一系列方法,而无需自己维护方法列表。 即使在今天,委托的多播功能在桌面开发

.NET使用CsvHelper快速读取和写入CSV文件

前言 在日常开发中使用CSV文件进行数据导入和导出、数据交换是非常常见的需求,今天我们来讲讲在.NET中如何使用CsvHelper这个开源库快速实现CSV文件读取和写入。 CsvHelper类库介绍 CsvHelper是一个.NET开源、快速、灵活、高度可配置、易于使用的用于读取和写入CSV文件的类

.NET使用原生方法实现文件压缩和解压

前言 在.NET中实现文件或文件目录压缩和解压可以通过多种方式来完成,包括使用原生方法(System.IO.Compression命名空间中的类)和第三方库(如:SharpZipLib、SharpCompress、K4os.Compression.LZ4等)。本文我们主要讲的是如何使用.NET原生方

如何更改.NET中的默认时区?

除了"在操作系统中修改时区信息,然后重启.NET应用程序,使其生效"之外。如何在不修改操作系统时区的前提下,修改.NET中的默认时区呢? 这是一位 同学兼同事 于5月21日在技术群里问的问题,我当时简单地研究了一下,就写出来了。 现在写文章分享给大家,虽然我觉得这种需求非常小众,几乎不会有人用到。

三个编程思想:面向对象编程、面向接口编程、面向过程编程【概念解析系列_1】【C# 基础】

对于 .Net 中的编程思想还是十分重要的,也是编码出高效的程序的基础!