• Dotnet Core下的Channel, 你用了吗?


    今天给大家分享一个微软官方的好东西:Channel。

    前言

    今天给大家分享一个微软官方的生产者/消费者方案的特性解决:Channel。

    Channel在System.Threading.Channels命名空间下,Core 2.1使用时,需要从Nuget上安装。

    % dotnet add package System.Threading.Channels

    而在Core 3.0 preview 7开始,就直接包含在框架中了。

    这是一个相对较新的特性。从Core 2.1开始加入,现在版本是5.0.0(嗯,这个版本号有点骗人,Channel的第一个版本就是4.5.0)。

    Channel能做什么?

    逻辑上,Channel实际就是一个高效的、线程安全的队列,支持在生产者和消费者之间传递数据。

    利用Channel,通过发布和订阅,可以将生产者和消费者分开。生产者Producer负责接收请求,并写入Channel,而消费者Consumer为每个进入Channel的数据执行处理。这样做,一方面可以使生产者和消费者并行工作来提高性能,另一方面,可以通过创建更多的生产者或消费者来提高应用的吞吐量。

        为防止非授权转发,这儿给出本文的原文链接:https://www.cnblogs.com/tiger-wang/p/14068973.html

    下面,我们以一个实际例子,来解释这个特性。

    创建Channel

    Channel提供了一个静态Channel类,提供了两个公开方法来创建两种类型的Channel。

    • CreateUnbounded - 创建一个具有无限容量的Channel。

    • CreateBounded - 创建一个具有有限容量的Channel。

    人通常来说,这两种方式使用上没有太大的区别。实际应用中,具体要看生产和消费的速度,以及期望产生的结果。有限容量的Channel,容量是有上限的,到达上限后,可以让生产者非阻塞等待消费者使用并释放Channel容量后再继续。这种方式,好处是可以控制生产的速度,控制系统资源的使用,缺点也是。因为控制速度意味着生产速度会被限制,甚至停止。而无限容量,生产者可以全速进行生产。但也有缺点,如果消费者的消费速度低于生产者,Channel的资源使用会无限增加,会有服务器资源耗尽的可能。

    今天的例子,我们使用无限Channel。

    var channel = Channel.CreateUnbounded<string>();

    非常简单的一行代码,就创建了一个无限容量的Channel。

    我们定义这个Channel用来保存字符串对象。

    创建方法是一个通用的工厂方法,所以我们可以为需要使用的任何类型的对象和数据创建Channel。

    Channel有两个属性:阅读器返回ChannelReader,写入器返回ChannelWriter。

    写入Channel

    使用写入器ChannelWriter,可以对Channel进行写入操作。ChannelWriter提供了以下几个方法:

    • WriteAsync - 异步写入
    • WaitToWriteAsync - 非阻塞等待,直到有空间可写入时或Channel关闭时,返回true/false
    • TryWrite - 尝试写入
    • Complete - 标记Channel为关闭,并不再写入数据到该Channel
    • TryComplete - 尝试标记Channel为关闭。

    这几个方法很容易理解,就不解释了。

    在本文的例子里,我用了:

    await channel.Writer.WriteAsync("New message");

    读取Channel

    使用阅读器ChannelReader从Channel进行数据的读取。也提供了几个方法:

    • ReadAsync - 异步读取
    • ReadAllAsync - 异步读取Channel中的所有数据
    • TryRead - 尝试读取
    • WaitToReadAsync - 非阻塞等待,直到有数据可读取或Channel关闭时,返回true/false

    不同的消费者模式,会用到不同的读取方法。这个根据经验来写就好。

    本文的例子中,我是采用WaitToReadAsync和ReadAsync配合来使用的:

    while (await ChannelReader.WaitToReadAsync())
    {
        if (ChannelReader.TryRead(out var timeString))
        {
              /***/
        }
    }

    WaitToReadAsync是一个非阻塞等待,在有消息可读或Channel关闭时,才会唤醒并继续。

    考虑到有多个消费者的情况,有可能别的线程已经进行了读取,这儿使用TryRead进行读取操作。

    要注意:数据的同步工作是由Channel进行管理的。Channel会确保多个消费者不会读到相同的数据。Channel同时也管理数据的次序。

    示例代码

    今天的示例代码我放到了Github上。链接是文章最后。

    这个例子中,我做了三个场景。

    首先是Channel。我使用了无限Channel。然后是创建生产者和消费者。数据传输过程就简单化了,生产者只简单将一个字符串写入到Channel。消费者也是,简单等待并从Channel读取数据字符串,写入控制台。

    三个场景分别是:

    单一生产者/单一消费者

    这个例子中,创建了一个生产者和一个消费者。两者的任务都是并发启动的。

    里面的延时,是用来模拟工作负载的。

    多个生产者/单一消费者

    这个例子中有两个生产者。通常在应用中有多个生产者时,我们需要确保生产与单个消费者所能处理的消息数量大致相当,这样能更好地利用服务器资源。

    单一生产者/多个消费者

    这个其实是应用中最常见的情况,就是产生消息很快,但处理工作相关较慢,而且工作也更密集。这种情况,实际应用中我们可以通过扩大消费者数量来满足生产的需求。

    总结

    最近的项目在做一个大数据的采集,用到了一些Channel的技术。然后发现网上这部分内容很少,就做了个例子,写了这个文章。

    Channel内容本身并不多,但用着很方便,而且实际应用中,比想像的更强大。它可以简化很多生产者/消费者模式的使用,而且,任务间交换数据,使用Channel会更方便,更直接。

    示例代码在:https://github.com/humornif/Demo-Code/tree/master/0033/demo

     

     


    微信公众号:老王Plus

    扫描二维码,关注个人公众号,可以第一时间得到最新的个人文章和内容推送

    本文版权归作者所有,转载请保留此声明和原文链接

  • 相关阅读:
    C# 消息队列 RabbitMQ
    C# webapi简单学习
    Navicat Premium 12注册机使用教程
    .net WCF简单练习
    MSDN 我告诉你(资源库)
    Dapper查询返回Datatable
    day55 无连接,无状态,会话跟踪、cookie、django中操作cookie、session、django中操作session
    day54 锁和事务、ajax、中间件
    day53 url别名反向解析、ORM多表操作、聚合查询、分组查询、F查询、Q查询
    day52
  • 原文地址:https://www.cnblogs.com/tiger-wang/p/14068973.html
Copyright © 2020-2023  润新知