Coroutine的原理以及实现
最近在写WinForm,在UI界面需要用到异步的操作,比如加载数据的同时刷系进度条,WinForm提供了不少多线程的操作,
但是多线程里,无法直接修改主线程里添加的UI的get/set属性访问器(可以通过关闭线程安全警告来暴力操作,但是不推荐),
另外的方法就是利用BackgroundWorker来实现线程间通信,主线程实现UI的刷系操作,其他线程通过向主线程发送ProgressChanged来通知主线程刷新UI。
实在是不太想使用多线程,于是想到了Unity的协程,单线程实现的异步操作,简单的反编译了Coroutine的源码跟上网搜了下资料,大概的弄懂了下它的原理:
参考文章:
https://blog.csdn.net/zhou8jie/article/details/49024791
https://blog.csdn.net/qq_30695651/article/details/79105332
1.Coroutine最关键的一个地方,利用到了IEnumerator接口,它是一个迭代器,你可以把它当成指向一个序列的某个节点的指针,它提供了两个重要的接口,分别是Current(返回当前指向的元素)和MoveNext()(将指针向前移动一个单位,如果移动成功,则返回true,如果已经移动到末尾,则返回false)
关于IEnumerator参考:
C#--IEnumerable 与 IEnumerator 的区别
2.yield,yield return 返回,可以返回null,true/false,或者一个IEnumerator
3.所以协程的内部,你可以想象成一条Action执行序列,例如:
Action1 -> Action2 -> ...
在执行完Action1的时候,返回true,该协程被挂起,并记录当前位置,下一帧,继续从上次的位置往下执行,直到执行完所有操作,返回false,此时执行完毕。
思想有点像行为树,同样是执行到Action,通过返回值决定是不是要挂起,只不过行为树没帧都是从头遍历,而协程只会从最后的位置继续执行。
4.因此不要以为协程能实现异步就跟多线程一样,他只是跟多线程一样可以并行执行,原理就是把步骤分割然后每帧调用其中的一些,实际上还是单线程的,如果某个动作占用太多的运算,
还是会给主线程造成严重影响的。
自己手动实现一个协程:
using System.Collections;
using System.Collections.Generic; namespace Com.Coroutine
{ public class Coroutine
{ internal IEnumerator m_Routine; internal IEnumerator Routine
{
get { return m_Routine; }
} internal Coroutine()
{
} internal Coroutine(IEnumerator routine)
{
this.m_Routine = routine;
} internal bool MoveNext()
{ var routine = m_Routine.Current as Coroutine; if (routine != null)
{
if (routine.MoveNext())
{
return true;
}
else if (m_Routine.MoveNext())
{
return true;
}
else
{
return false;
}
}
else if (m_Routine.MoveNext())
{
return true;
}
else
{
return false;
}
}
} // use this as a template for functions like WaitForSeconds()
public class WaitForCount : Coroutine
{
int count = ;
public WaitForCount(int count)
{
this.count = count;
this.m_Routine = Count();
} IEnumerator Count()
{
while (--count >= )
{
System.Console.WriteLine(count);
yield return true;
}
}
} // use this as the base class for enabled coroutines
public class CoroutineManager
{ internal List<Coroutine> m_Coroutines = new List<Coroutine>(); // just like Unity's MonoBehaviour.StartCoroutine
public Coroutine StartCoroutine(IEnumerator routine)
{
var coroutine = new Coroutine(routine);
m_Coroutines.Add(coroutine);
return coroutine;
} // call this every frame
public void ProcessCoroutines()
{
for (int i = ; i < m_Coroutines.Count; )
{
var coroutine = m_Coroutines[i];
if (coroutine.MoveNext())
{
++i;
}
else if (m_Coroutines.Count > )
{
m_Coroutines[i] = m_Coroutines[m_Coroutines.Count - ];
m_Coroutines.RemoveAt(m_Coroutines.Count - );
}
else
{
m_Coroutines.Clear();
break;
}
}
}
}
}
Coroutine的原理以及实现的更多相关文章
- Unity协程(Coroutine)原理深入剖析
Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 其实协程并没有那么复杂,网上很多地方都说是多 ...
- Unity协程(Coroutine)原理深入剖析再续
Unity协程(Coroutine)原理深入剖析再续 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 前面已经介绍过对协程(Coroutine ...
- 【转】Unity协程(Coroutine)原理深入剖析
Unity协程(Coroutine)原理深入剖析 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 记得去年6月份刚开始实习的时候,当时要我写网 ...
- Unity协程(Coroutine)原理深入剖析(转载)
记得去年6月份刚开始实习的时候,当时要我写网络层的结构,用到了协程,当时有点懵,完全不知道Unity协程的执行机制是怎么样的,只是知道函数的返回值是IEnumerator类型,函数中使用yield r ...
- c coroutine
今天看了下云风c coroutine 代码 博客,发现 coroutine 实现原理其实还比较简单,就用户态栈切换,只需要几十行汇编,特别轻量级. 具体实现 1. 创建一个coroutine: 也就 ...
- libcoro:在c++中支持coroutine
起因 在第一个版本的libtnet开发完成之后,我一直在思考如何让异步方式的网络编程更加简单. 虽然libtnet通过c++ shared_ptr以及function等技术很大程度上面解决了异步代码编 ...
- unity协程coroutine浅析
转载请标明出处:http://www.cnblogs.com/zblade/ 一.序言 在unity的游戏开发中,对于异步操作,有一个避免不了的操作: 协程,以前一直理解的懵懵懂懂,最近认真充电了一下 ...
- yield学习续:yield return迭代块在Unity3D中的应用——协程
必读好文推荐: Unity协程(Coroutine)原理深入剖析 Unity协程(Coroutine)原理深入剖析再续 上面的文章说得太透彻,所以这里就记一下自己的学习笔记了. 首先要说明的是,协程并 ...
- tornado 异步
引言 注:正文中引用的 Tornado 代码除特别说明外,都默认引用自 Tornado 4.0.1. tornado.gen 模块是一个基于 python generator 实现的异步编程接口.通过 ...
随机推荐
- 协程demo,1异步爬网页 2异步socket请求
一.异步爬网页 ''' 协程并发爬网页 ''' from urllib import request import gevent,time from gevent import monkey # 让g ...
- 【MOS】在不同版本和平台之间进行还原或复制 (文档 ID 1526162.1)--跨版本恢复
参考链接:http://blog.itpub.net/26736162/viewspace-1549041/
- 18 python 初学(time、random 模块)
# _author: lily # _date: 2019/1/13 import time import datetime print(help(time)) # print(time.time() ...
- 2.[Andriod]Andriod Studio结合Visual Studio Emulator for Android调试Android App
0. 工欲善其事必先利其器 上一篇博客对比了一下Android和WinPhnoe的布局容器,后续篇章重点放在Android的开发上了. 说到开发就绕不开调试程序,调试Android App我们有2种选 ...
- maven eclipse web 项目 问题 cannot change version of project facet dynamic web module to 3.0
cannot change version of project facet dynamic web module to 3.0 修改 web.xml 头部 xsi:schemaLocation=&q ...
- jeecg开发环境搭建
Maven安装 步骤见:https://www.cnblogs.com/dyh004/p/8523260.html 修改Maven仓库 1.修改maven仓库存放位置 修改maven仓库存放位置:找到 ...
- Ubuntu软件的安装与删除
- Flask 快速使用 进阶—— (2)
案例:可配置发送信息的系统 假如我们有这样的一个系统,可以发送短息,邮件和微信,后期可能还会增加一些平台,怎么才可以做到快速切换的去使用某种功能呢,在这里我会通过在配置文件中简单的配置就可以实现 在项 ...
- 关于token登录逻辑分析
前言: token登录上一家公司也写过,迷迷糊糊的, 现在做一个APP,需求为每次调用接口都会传token,登录注册等特殊的除外, 逻辑整理一下还是比较简单的 主要的问题还是,如何在框架中找到较好的插 ...
- 记一次innobackupex备份恢复数据库过程
简介:以前备份都是通过mysqldump备份数据库的,由于是逻辑备份,所以采用这种备份方式数据是很安全的,跨平台.版本都很容易.凡事有利必有弊,逻辑备份在你数据库比较大时,备份.恢复数据所耗费的时间也 ...