概述

ET框架很多地方都用到了异步,例如资源加载、AI、Actor模型等等。ET框架对C#的异步操作进行了一定程度的封装和改造,有一些特点:

  • 显式的或者说强调了使用C#异步实现协程机制(其实C#的异步编程天生就能实现这种用法)
  • 强制单线程异步
  • 没有使用C#库的Task,自己实现了ETTask等类
  • 实现了协程锁

为了更好的理解下面的内容,推荐先看一下之前写的这两篇文章:

ETTask

C# 的异步函数有三个返回值(现在好像.NET7又多了一个ValueTask):Task,Task<T>,void,对应的,ET框架也一样对应实现了:ETTask,ETTask/,ETVoid,其实现相比C#简化了一些逻辑,并添加一些新的特性以适应ET框架,其实使用起来是差不多的。为了实现ETTask,也实现了对应AsyncTaskCompletedMethodBuilder的AsyncETTaskCompletedMethodBuilder等类(其实还C#原来的逻辑差不太多,有兴趣可以看下上述C# 异步编程的链接)。

ETTask添加了一些特性:

  • 支持对象池
  • 显式强调协程
[DebuggerHidden]
private async ETVoid InnerCoroutine()
{
await this;
} [DebuggerHidden]
public void Coroutine()
{
InnerCoroutine().Coroutine();
}

可以看到这里的所谓协程Coroutine,其实等效于 await task,只是平平无奇的异步调用罢了

  • 异常消息打印

同步上线文 SynchronizationContext

C#异步编程在大多数情况下会使用多线程,ET的异步操作例如定时器等,使用多线程的开销相比较大,且ET框架是多进程,性能是分摊到多个进程中。所以ET使用了单线程的异步。

ThreadSynchronizationContext继承自SynchronizationContext,在构造初始化是会把自身设为当前SynchronizationContext.Current,重写了Post(异步消息分派到同步上下文)方法,来改写异步消息的分派到当前线程(就是进入队列)。

而异步函数在执行时,会获取当前上下文(__builder.AwaitUnsafeOnCompleted方法会调用GetCompletionAction,内部调用ExecutionContext.FastCapture(),这个方法内部捕获SynchronizationContext,感兴趣可以关键词搜索下)

public class ThreadSynchronizationContext : SynchronizationContext
{
// 线程同步队列,发送接收socket回调都放到该队列,由poll线程统一执行
private readonly ConcurrentQueue<Action> queue = new ConcurrentQueue<Action>(); private Action a; public void Update()
{
while (true)
{
if (!this.queue.TryDequeue(out a))
{
return;
} try
{
a();
}
catch (Exception e)
{
Log.Error(e);
}
}
} public override void Post(SendOrPostCallback callback, object state)
{
this.Post(() => callback(state));
} public void Post(Action action)
{
this.queue.Enqueue(action);
}
} public class MainThreadSynchronizationContext: Singleton<MainThreadSynchronizationContext>, ISingletonUpdate
{
private readonly ThreadSynchronizationContext threadSynchronizationContext = new ThreadSynchronizationContext(); public MainThreadSynchronizationContext()
{
SynchronizationContext.SetSynchronizationContext(this.threadSynchronizationContext);
} public void Update()
{
this.threadSynchronizationContext.Update();
} public void Post(SendOrPostCallback callback, object state)
{
this.Post(() => callback(state));
} public void Post(Action action)
{
this.threadSynchronizationContext.Post(action);
}
} // MainThreadSynchronizationContext.Instance.Update()
Game.Update();

ThreadSynchronizationContex由包裹的MainThreadSynchronizationContext驱动更新,MainThreadSynchronizationContext是个单件,由外面驱动。更新Update方法会把队列里的委托取出执行。

SynchronizationContext

假设有两个线程,一个UI线程,一个后台线程,一个业务先在后台线程计算数据,然后在UI线程中刷新显示数据,显然不同的线程其上下文环境是不同的,两个线程的通信可以使用SynchronizationContext完成。

SynchronizationContext官方文档 https://learn.microsoft.com/zh-CN/dotnet/api/system.threading.synchronizationcontext?view=netcore-3.0

协程锁

多线程编程,对公共资源的访问要加锁,以保证数据访问的安全。类似的,在ET的异步编程中,从虽然上文中可以了解到ET的异步其实是单线程的,从代码运行的层面其实是一个线程以某种顺序处理一个个的任务,但是这种“顺序”并不可控。ET这里的协程锁其实就是使用某个key,对所有用这个key包裹的代码段推入一个队列,只有前面的代码段执行结束才能执行后面的代码。

这看起来和C#平时用的lock(object),其实只是用法上比较像,其实在实现细节是有根本的差距的:简单来说。ET实现的协程锁是一种用户态的锁,不会造成内核态/用户态的切换。而lock是一种C#语法糖,在编译时其实是通过Monitor监视器实现的,会涉及到内核转换。一个线程上可能会运行成百上千个协程,如果这个线程被挂起,那么有可能造成很多协程Delay,可能造成灾难性的后果。

结构类图:



时序图:



结合ET工程官方的一个用法:

public static async ETTask<T> Query<T>(this DBComponent self, long id, string collection = null) where T : Entity
{
using (await CoroutineLockComponent.Instance.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))
{
IAsyncCursor<T> cursor = await self.GetCollection<T>(collection).FindAsync(d => d.Id == id); return await cursor.FirstOrDefaultAsync();
}
}

可以看到协程锁是被using包裹的,即{}包裹的代码块运行结束,协程锁会被dispose。

先来看当第一次调用Wait时会直接返回,当第一次的锁没有被dispose时,后面获取锁时会进入队列。当前面的锁被dispose时,会通知队列中后面一个锁在下一次Update时被Notify,SetResult获取到锁,其所属的代码段得以执行。

ET框架6.0分析二、异步编程的更多相关文章

  1. Tornado 高并发源码分析之六---异步编程的几种实现方式

    方式一:通过线程池或者进程池 导入库futures是python3自带的库,如果是python2,需要pip安装future这个库 备注:进程池和线程池写法相同 from concurrent.fut ...

  2. 异步编程系列06章 以Task为基础的异步模式(TAP)

    p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提 ...

  3. 深入浅出node(4) 异步编程

    一)函数式编程基础 二)异步编程的优势和难点 2.1 优势 2.2 难点 2.2.1 异常处理 2.2.2 函数嵌套过深 2.2.3 阻塞 2.2.4 多线程编程 2.2.5 异步转同步 三)异步编程 ...

  4. Java8函数之旅 (八) - 组合式异步编程

    前言 随着多核处理器的出现,如何轻松高效的进行异步编程变得愈发重要,我们看看在java8之前,使用java语言完成异步编程有哪些方案. JAVA8之前的异步编程 继承Thead类,重写run方法 实现 ...

  5. 趣学Spring:一文搞懂Aware、异步编程、计划任务

    你好呀,我是沉默王二,一个和黄家驹一样身高,刘德华一样颜值的程序员(不信围观朋友圈呗).从 2 位偶像的年纪上,你就可以断定我的码龄至少在 10 年以上,但实话实说,我一直坚信自己只有 18 岁,因为 ...

  6. .Net Core自实现CLR异步编程模式(Asynchronous programming patterns)

    最近在看一个线程框架,对.Net的异步编程模型很感兴趣,所以在这里实现CLR定义的异步编程模型,在CLR里有三种异步模式如下,如果不了解的可以详细看MSDN 文档Asynchronous progra ...

  7. 新手浅谈Task异步编程和Thread多线程编程

    初学Task的时候上网搜索,看到很多文章的标题都是task取代thread等等相关,我也一直以为task和thread是一类,其实task是.net4.0提出的异步编程,在之前.net1.0有dele ...

  8. Spark RPC框架源码分析(二)RPC运行时序

    前情提要: Spark RPC框架源码分析(一)简述 一. Spark RPC概述 上一篇我们已经说明了Spark RPC框架的一个简单例子,Spark RPC相关的两个编程模型,Actor模型和Re ...

  9. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  10. 【转】【C#】C# 5.0 新特性——Async和Await使异步编程更简单

    一.引言 在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,对于C#又有了新特性的增加--就是C#5.0中async和await两个关键字,这两 ...

随机推荐

  1. mysql 存储国过程实现竖表变横表(将行数据值变为字段)

    示例: 表结构如下: CREATE TABLE `pressure` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `presurena ...

  2. mysql zip安装步骤

    1. 官网下载社区版 https://dev.mysql.com/downloads/mysql/ 版本5.7或者8.0 2. 解压到指定的目录. 3.创建my.ini文件,编辑内容: [mysqld ...

  3. 一篇博客带你学会MyBatis

    概念 MyBatis是一款持久层框架,用于简化JDBC操作(JavaEE三层架构:表现层(用来做页面的代码),业务层(业务逻辑代码),持久层(对数据库操作的代码))(框架就是一个半成品软件,是一套可重 ...

  4. Android笔记--查询联系人

    查询联系人 先在raw_contacts表里面查到每个联系人的不同的id,然后再根据各个id去查询各个联系人的详细信息 然后利用id得到相应的uri的值: 之后,就直接根据uri查询各个联系人的详细信 ...

  5. linux服务器已知目录下根据特定字符串查找文件

    很久不用的一个项目的测试环境迁移后,启动项目时,发现日志一直报错,在opt/下缺少一个jar包,思路是:查看配置文件,想找到具体是哪个配置文件写了需要用到这个jar包,然后修改到正确路径下重新启动尝试 ...

  6. 红黑树(map与unorder_map)B B+树

    红黑树(map) 这个里面有插入的几种方式:红黑树性质的理解 先说性质,1 每个节点要么红要么黑,2 一个节点为红色,左右两个孩子都是黑, 3 根节点是黑, 4 每个叶子(nil)节点都是黑色, 5 ...

  7. 服务器资源监测脚本(网卡、MEM、CPU)

    #-*- coding: utf-8 -*- #!/usr/bin/python ''' 用法: sar [ 选项 ] [ <时间间隔> [ <次数> ] ] 主选项和报告: ...

  8. Golang 实现 RTP

    在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. ...

  9. 基于深度学习的鸟类检测识别系统(含UI界面,Python代码)

    摘要:鸟类识别是深度学习和机器视觉领域的一个热门应用,本文详细介绍基于YOLOv5的鸟类检测识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择各种鸟类图 ...

  10. 刷爆 LeetCode 双周赛 100,单方面宣布第一题最难

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 上周末是 LeetCode 第 100 场双周赛,你参加了吗?这场周赛整体没有 Hard ...