zoukankan      html  css  js  c++  java
  • 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;
                    }
                }
            }
        }
    }
  • 相关阅读:
    zoj 3627#模拟#枚举
    Codeforces 432D Prefixes and Suffixes kmp
    hdu 4778 Gems Fight! 状压dp
    CodeForces 379D 暴力 枚举
    HDU 4022 stl multiset
    手动转一下田神的2048
    【ZOJ】3785 What day is that day? ——KMP 暴力打表找规律
    poj 3254 状压dp
    C++中运算符的优先级
    内存中的数据对齐
  • 原文地址:https://www.cnblogs.com/jeason1997/p/9502744.html
Copyright © 2011-2022 走看看