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 = 0; public WaitForCount(int count) { this.count = count; this.m_Routine = Count(); } IEnumerator Count() { while (--count >= 0) { 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 = 0; i < m_Coroutines.Count; ) { var coroutine = m_Coroutines[i]; if (coroutine.MoveNext()) { ++i; } else if (m_Coroutines.Count > 1) { m_Coroutines[i] = m_Coroutines[m_Coroutines.Count - 1]; m_Coroutines.RemoveAt(m_Coroutines.Count - 1); } else { m_Coroutines.Clear(); break; } } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具