【C# Task】System.Threading.Channels 生产者和消费者模式
前言
今天给大家分享一个微软官方的生产者/消费者方案的特性解决:Channel。
Channel在% 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
【C# Task】System.Threading.Channels 生产者和消费者模式的更多相关文章
- java生产者与消费者模式
前言: 生产者和消费者模式是我们在学习多线程中很经典的一个模式,它主要分为生产者和消费者,分别是两个线程, 目录 一:生产者和消费者模式简介 二:生产者和消费者模式的实现 声明:本例来源于java经典 ...
- condition版生产者与消费者模式
1.简介 在爬虫中,生产者与消费者模式是经常用到的.我能想到的比较好的办法是使用redis或者mongodb数据库构造生产者消费者模型.如果直接起线程进行构造生产者消费者模型,线程容易假死,也难以构造 ...
- Java并发编程(4)--生产者与消费者模式介绍
一.前言 这种模式在生活是最常见的,那么它的场景是什么样的呢? 下面是我假象的,假设有一个仓库,仓库有一个生产者和一个消费者,消费者过来消费的时候会检测仓库中是否有库存,如果没有了则等待生产,如果有就 ...
- Java多线程设计模式(2)生产者与消费者模式
1 Producer-Consumer Pattern Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不 ...
- 【爬虫】Condition版的生产者和消费者模式
Condition版的生产者和消费者模式 threading.Condition 在没有数据的时候处于阻塞状态,有数据可以使用notify的函数通知等等待状态的线程运作 threading.Condi ...
- 【爬虫】Load版的生产者和消费者模式
''' Lock版的生产者和消费者模式 ''' import threading import random import time gMoney = 1000 # 原始金额 gLoad = thre ...
- java 线程并发(生产者、消费者模式)
线程并发协作(生产者/消费者模式) 多线程环境下,我们经常需要多个线程的并发和协作.这个时候,就需要了解一个重要的多线程并发协作模型“生产者/消费者模式”. Ø 什么是生产者? 生产者指的是负责生产数 ...
- java进阶(40)--wait与notify(生产者与消费者模式)
文档目录: 一.概念 二.wait的作用 三.notify的作用 四.生产者消费者模式 五.举例 ---------------------------------------分割线:正文------ ...
- 使用libuv实现生产者和消费者模式
生产者和消费者模式(Consumer + Producer model) 用于把耗时操作(生产线程),分配给一个或者多个额外线程执行(消费线程),从而提高生产线程的响应速度(并发能力) 定义 type ...
随机推荐
- ROS之arduino交互
一.第一种安装方式(不支持自定义消息) 第一步打开官网 http://wiki.ros.org/rosserial_arduino/Tutorials/Arduino%20IDE%20Setup 第二 ...
- Go 面向对象之结构体
#### Go 面向对象之结构体最近有四天没更新公众号,有一些事情耽误了,生活就是这样,总会一些事情让人措不及防; ***山浓水浅,坐看流年***1. Go 也支持面向对象编程(OOP) 但是它和传统 ...
- 女朋友让我深夜十二点催她睡觉,我有Python我就不干
事情是这样的:今天晚上,女朋友让我十二点催她睡觉. 不过,可是我实在太困了,熬不下去-- 是吧?女朋友哪有睡觉重要? 但,女朋友的命令,我是不敢违抗的-- 但是睡觉也不能缺! 这时候我们该怎么办呢?是 ...
- 尚硅谷全套课件整理:Java、前端、大数据、安卓、面试题
目录 Java 尚硅谷 IT 精英计划 JavaSE 内部学习笔记.pdf 尚硅谷 Java 基础实战之银行项目.pdf 尚硅谷 Java 技术之 JDBC.pdf 尚硅谷 Java 技术之 Java ...
- Objects、Arrays、Collectors、System工具类
Objects类 定义 位于java.util包中,JDK1.7以后操作对象的类,对对象的空,对象是否相等进行判断. 常用方法 1.public static boolean equals(Objec ...
- War包是什么??
感谢大佬: https://blog.csdn.net/Stitch__/article/details/88091745 https://www.jianshu.com/p/3b5c45e8e5bd ...
- nodejs process uncaughtException
用过Node一段时间之后,发现那些在事件主循环里碰到的异常会导致Node进程退出.在许多应用场景下,特别是对那些希望永不当机的服务器程序来说,这都是不接受的.uncaughtException事件会提 ...
- 一行代码轻松修改 Text Field 和 Text View 的光标颜色 — By 昉
众所周知,Text Field 和 Text View 的光标颜色默认都是系统应用的那种蓝色,如图: 而在实际开发中为了让视觉效果更统一,我们可能会想把那光标的颜色设置成和界面色调一致的颜色.其实在 ...
- Docker私有仓库与Harbor部署使用
Docker私有仓库与Harbor部署使用 目录 Docker私有仓库与Harbor部署使用 一.本地私有仓库 1. 下载registry镜像 2. 在daemon.json文件中添加私有镜像仓库地址 ...
- java中将SimpleDateFormat类型转换成Date类型
try {String dateString = "2009-08-02 13:43:00";DateFormat df = SimpleDateFormat("yyyy ...