• 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 的区别

    IEnumerator Interface

    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;
                    }
                }
            }
        }
    }
  • 相关阅读:
    Nokia N78拍照最佳设置!
    摩托Milestone购机鉴别篇
    sdf
    js随笔
    jssip无法识别以数字开始的域
    ASP.NET页面生命周期与优化
    在SQLServer2005中使用全文搜索
    简析正则表达式
    《WCF编程》之错误
    《WCF编程》之实例管理
  • 原文地址:https://www.cnblogs.com/jeason1997/p/9502744.html
Copyright © 2020-2023  润新知