C#异步案例一则
场景
生产者和消费者队列, 生产者有多个, 消费者也有多个, 生产到消费需要异步.
下面用一个Asp.NetCore Web-API项目来模拟
创建两个API, 一个Get(), 一个Set(), Get返回一个字符串, Set放入一个字符串, Get返回的就是Set进去的字符串.
实现如下:
[Route("api/[controller]/[action]")]
public class FooController : Control
{
IMessageQueue _mq;
public FooController(IMessageQueue mq)
{
_mq = mq;
}
[HttpGet]
public string Get()
{
string str = _mq.ReadOne<string>();
return str;
}
[HttpGet]
public void Set(string v)
{
_mq.WriteOne(v);
}
}
public interface IMessageQueue
{
T ReadOne<T>();
void WriteOne<T>(T value);
}
public class MessageQueue: IMessageQueue
{
private object _value;
public T ReadOne<T>()
{
return (T)_value;
}
public void WriteOne<T>(T value)
{
_value = value;
}
}
接着在StartUp中把IMessageQueue给注入了.
services.AddSingleton<IMessageQueue, MessageQueue>();
运行后, 先调用/api/foo/set/?v=xxx, 再调用/api/foo/get/
可以看到成功返回了xxx
第二步, value字段改为队列:
使set进去的值不会被下一个覆盖, get取队列最前的值
为了线程安全, 这里使用了ConcurrentQueue<T>
代码如下:
public class MessageQueue: IMessageQueue
{
private readonly ConcurrentQueue<object> _queue = new ConcurrentQueue<object>(); public T ReadOne<T>()
{
_queue.TryDequeue(out object str);
return (T)str ;
} public void WriteOne<T>(Tvalue)
{
_queue.Enqueue(value);
}
}
那么此时, 只要get不断地轮询, 就可以取到set生产出来的数据了.
调用/api/foo/set/
三, 异步阻塞
再增加需求, 调换get和set的顺序,先get后set模拟异步, (我这里的demo是个web-api会有http请求超时之类的...假装不存在)我想要get调用等待有数据时才返回.
也就是说我想要在浏览器地址栏输入http://localhost:5000/api/foo/get/之后会不断地转圈直到我用set接口放入一个值
方案A: while(true), 根本无情简直无敌, 死等Read() != null时break; 为防单核满转加个Thread.Sleep();
方案B: Monitor, 一个Wait()一个Exit/Release();
但是以上两个方案都是基于Thread的, .Net4.0之后伴随ConcurrentQueue一起来的还有个BlockingCollection<T>相当好用
方案C: 修改后代码如下:
public class MessageQueue : IMessageQueue
{
private readonly BlockingCollection<object> _queue = new BlockingCollection<object>(new ConcurrentQueue<object>()); public T ReadOne<T>()
{
var obj = _queue.Take();
return (T)obj;
} public void WriteOne<T>(T value)
{
_queue.Add(value);
}
}
此时, 如果先get, 会阻塞等待set; 如果已经有set过数据就会直接返回队列中的数据. get不会无功而返了. 基于这个类型, 可以实现更像样的订阅模型.
扩展RPC
这里的set是生产者, get是消费者, 那如果我的这个生产者并不单纯产生数据返回void而是需要等待一个结果的呢? 此时订阅模型不够用了, 我需要一个异步的RPC .
比如有个Ask请求会携带参数发起请求, 并等待, 知道另外有个地方处理了这个任务产生结果, ask结束等待返回这个结果answer.
我可以回头继续用方案A或B, 但连.net4.0都已经过去很久了, 所以应该用更好的基于Task的异步方案.
代码如下, 首先新增两个接口:
public interface IMessageQueue
{
void Respond<TRequest, TResponse>(Func<TRequest, TResponse> func);
Task<TResponse> Rpc<TRequest, TResponse>(TRequest req); T ReadOne<T>();
void WriteOne<T>(T data);
}
接着定义一个特殊的任务类:
public class RpcTask<TRequest, TResponse>
{
public TaskCompletionSource<TResponse> Tcs { get; set; }
public TRequest Request { get; set; }
}
实现刚才新加的两个接口:
public Task<TResponse> Rpc<TRequest, TResponse>(TRequest req)
{
TaskCompletionSource<TResponse> tcs = new TaskCompletionSource<TResponse>();
_queue.Add(new RpcTask<TRequest, TResponse> { Request = req, Tcs = tcs});
return tcs.Task;
} public void Respond<TRequest, TResponse>(Func<TRequest, TResponse> func)
{
var obj = _queue.Take();
if(obj is RpcTask<TRequest, TResponse> t)
{
var response = func(t.Request);
t.Tcs.SetResult(response);
}
}
同样的, 写两个Web API接口, 一个请求等待结果 一个负责处理工作
[HttpGet]
public async Task<string> Ask(string v)
{
var response = await _mq.Rpc<MyRequest, MyResponse>(new MyRequest { Id = v });
return $"[{response.DoneTime}] {response.Id}";
} [HttpGet]
public void Answer()
{
_mq.Respond<MyRequest, MyResponse>((req)=> new MyResponse { Id = req.Id, DoneTime = DateTime.Now });
}
上面还随便写了两个class作为请求和返回
public class MyRequest
{
public string Id { get; set; }
}
public class MyResponse
{
public string Id { get; set; }
public DateTime DoneTime { get; set; }
}
测试一下, 用浏览器或postman打开三个选项卡, 各发起一个Ask接口的请求, 参数v分别为1 2 3, 三个选项卡都开始转圈等待
然后再打开一个选项卡访问answer接口, 处理刚才放进队列的任务, 发起一次之前的三个选项卡之中就有一个停止等待并显示返回数据. 需求实现.
这里用到的关键类型是TaskCompletionSource<T>.
再扩展
如果是个分布式系统, 请求和处理逻辑不是在一个程序里呢? 那么这个队列可能也是一个单独的服务. 此时就要再加个返回队列了, 给队列中传输的每一个任务打上Id, 返回队列中取出返回之后再找到Id对于的TCS.SetResult()
C#异步案例一则的更多相关文章
- nodejs异步案例
const fs = require('fs'); fs.readFile('./test.txt', 'utf-8', (err, data) => { err ? console.error ...
- 深度学习_1_Tensorflow_2_数据_文件读取
tensorflow 数据读取 队列和线程 文件读取, 图片处理 问题:大文件读取,读取速度, 在tensorflow中真正的多线程 子线程读取数据 向队列放数据(如每次100个),主线程学习,不用全 ...
- 性能追击:万字长文30+图揭秘8大主流服务器程序线程模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul
本文为<高性能网络编程游记>的第六篇"性能追击:万字长文30+图揭秘8大主流服务器程序线程模型". 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让 ...
- 058.Python前端Django与Ajax
一 Ajax简介 AJAX(Asynchronous Javascript And XML)翻译成中文就是"异步Javascript和XML".即使用Javascript语言与服务 ...
- Ajax与ashx异步请求的简单案例
Ajax与ashx异步请求的简单案例: 前台页面(aspx): <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//E ...
- 一个完整的springmvc + ajaxfileupload实现图片异步上传的案例
一,原理 详细原理请看这篇文章 springmvc + ajaxfileupload解决ajax不能异步上传图片的问题.java.lang.ClassCastException: org.apache ...
- spring boot集成swagger,自定义注解,拦截器,xss过滤,异步调用,guava限流,定时任务案例, 发邮件
本文介绍spring boot集成swagger,自定义注解,拦截器,xss过滤,异步调用,定时任务案例 集成swagger--对于做前后端分离的项目,后端只需要提供接口访问,swagger提供了接口 ...
- vue路由异步组件案例
最近研究了vue性能优化,涉及到vue异步组件.一番研究得出如下的解决方案. 原理:利用webpack对代码进行分割是异步调用组件前提.异步组件在优先级上让位同步组件.下面介绍的是怎么实现异步组件. ...
- PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)
源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...
随机推荐
- Oracle大规模数据快速导出文本文件
哈喽,前几久,和大家分享过如何把文本数据快速导入数据库(点击即可打开),今天再和大家分享一个小技能,将Oracle数据库中的数据按照指定分割符.指定字段导出至文本文件.首先来张图,看看导出的数据是什么 ...
- WebSocket原理及技术简介
摘要: WebSocket用于在Web浏览器和服务器之间进行任意的双向数据传输的一种技术.WebSocket协议基于TCP协议实现,包含初始的握手过程,以及后续的多次数据帧双向传输过程.其目的是在We ...
- 用Python编写简单的发红包程序和计算器原理
用Python编写简单的发红包程序: 第一种解法:数轴方法解决 import random def red_packet(money,num): money = money * 100 #将钱数转换成 ...
- MySQL计划任务(事件调度器)
原文:http://www.cnblogs.com/c840136/articles/2388512.html 备忘; MySQL5.1.x版本中引入了一项新特性EVENT,顾名思义就是事件.定时任务 ...
- NOIP提高组/CSP-S复赛需掌握的算法
1.排序算法(快排.选择.冒泡.堆排序.二叉排序树.桶排序) 2.DFS/BFS 也就是搜索算法,剪枝务必要学! 学宽搜的时候学一下哈希表! 3.树 ①遍历 ②二叉树 ③二叉排序树(查找.生成.删除) ...
- Flex简记
display的flex属性的使用对象分为1.容器即包含有元素的元素,2.项目,即某一个元素.就容器而言,flex可以实现换行(flex-wrap),改变容器内元素的排列方向(flex-directi ...
- [考试反思]1005csp-s模拟测试61:休止
连续不知道多少场了,都是一场10名以内一场20以外...波动极大...还极有规律... 拿到这套题,看到T1大模拟无话可说. 然后考场上我觉得T2很简单....然后就码了两个半小时. T3数据水了暴力 ...
- [考试反思]0929csp-s模拟测试55:消逝
菜得过分. 面对T1的大板子不知所措,然后T2的贪心不小心把排序语句删了... T1这种大模板啊...其实我是觉得我能打出来的,然后先用一个小时码了一个2k. 然后做T2想贪心就出来了.十分钟码完T3 ...
- 七月月赛T3
题目背景 本题时空限制已经扩大 题目描述 人比人,气死人:鱼比鱼,难死鱼.小鱼最近参加了一个“比可爱”比赛,比的是每只鱼的可爱程度.参赛的鱼被从左到右排成一排,头都朝向左边,然后每只鱼会得到一个整数数 ...
- 安全路径——最短路径树+dsu缩边
题目描述 思路 首先想到$dijkstra$跑完之后$build$一棵最短路径树.要找到每个节点i到根的满足要求的最短路,考虑把一些非树边加进去. 对于非树边$(u,v)$,因为节点i上方的边被占领, ...