C# simula la corrutina de Unity3d

I. Resumen

Dado que Unity3d usa un solo subproceso al desarrollar juegos, para proporcionarles a los desarrolladores operaciones asincrónicas, los desarrolladores agregaron el concepto de corrutinas a Unity3d. Las corrutinas se usan mucho en Unity3d, y algunos grandes piensan que esto no es fácil de usar, es es mejor usar algunos complementos.

No hay una interfaz correspondiente a las rutinas en C#, pero podemos encapsular una. Verifiqué en Baidu, y los códigos para realizar estas funciones son más o menos similares. Para realizar múltiples esperas en un método, se utilizan conceptos relacionados con IEnumerator, como se muestra en el siguiente código:

IEnumerator Test()
{
    Console.WriteLine("开始");
    yield return new WaitForSeconds(2);
    Console.WriteLine("结束1");
    yield return new WaitForSeconds(2);
    Console.WriteLine("结束2");

    Console.WriteLine("完成");
}

Sin embargo, en el desarrollo original de C#, estas funciones no se usan en absoluto. Es fácil realizar esta función usando Thread + Thread.Sleep (tiempo de espera). Además, estas funciones también se pueden realizar usando asynchronous y await palabras clave. La única diferencia es que, los métodos asincrónicos propios de C# y otros, cancelar la ejecución de la espera es un poco más problemático y más complicado. Además, los subprocesos múltiples no se utilizan bien y es probable que aparezcan algunos errores repentinos. cantidad de código es grande, no es tan bueno.

De hecho, en Winform y otros desarrollos, los temporizadores se usan demasiado, lo que también es muy problemático:

1. Antes de cerrar el programa, si el temporizador no está cerrado, a veces el programa no se puede cerrar y siempre está atascado.

2. El temporizador se usa demasiado, el programa se ejecuta durante mucho tiempo y es fácil bloquearse.

3. No hay una gestión unificada del código del temporizador, lo cual es bastante confuso.Después de mucho tiempo, no recuerdo cuántos temporizadores usé.

Por lo tanto, usar corrutinas en el proyecto puede no ser una buena solución, pero la premisa es probar bien.

Para tutoriales relacionados con asincrónicos, puede consultar la publicación:

C# asíncrono/esperar el procesamiento del tiempo de espera de la tarea_Cómo salir después del tiempo de espera del procesamiento de c# después de que se inicie la tarea_Blog de Xiong Siyu-CSDN Blog

Para tutoriales relacionados con IEnumerator, puede consultar la publicación:

C# IEnumerator Usage_c#ienumerator_Blog de Xiong Siyu-Blog de CSDN

Dos, realizar la función

Cree una nueva biblioteca de clases, CoroutineLibrary, que es más conveniente para llamar desde otros proyectos en forma de dll.

Agregar una rutina de clase

using System.Collections;

namespace CoroutineLibrary
{
    public class Coroutine
    {
        private IEnumerator routine;

        //返回 false 当前的协程将会被从链表中移除
        public bool Next()
        {
            if (routine == null)
                return false;

            IWait wait = routine.Current as IWait;

            //如果当前延时还没有结束,会一直重复的调用 Tick
            bool timeIsOver = true;
            if (wait != null)
                timeIsOver = wait.Tick();

            if (!timeIsOver)
                return true;
            else
                //如果当前的延时已经结束,那么就移动到下一个迭代
                //如果成功移动到下一个迭代,则返回true,否则返回false
                return routine.MoveNext();
        }

        public Coroutine(IEnumerator routines)
        {
            routine = routines;
        }
    }

    /// <summary>
    /// 等待接口
    /// </summary>
    internal interface IWait
    {
        /// <summary>
        /// 每帧检测是否等待结束
        /// </summary>
        /// <returns></returns>
        bool Tick();
    }
}

La función principal de la clase Coroutine es verificar si la iteración actual ha llegado al tiempo especificado, por ejemplo, si el intervalo es de un segundo, el método Next se llamará continuamente para determinar si se debe ingresar a la siguiente iteración.

Agregar una clase WaitForSeconds

namespace CoroutineLibrary
{
    public class WaitForSeconds : IWait
    {
        private float waitTime = 0;

        bool IWait.Tick()
        {
            waitTime -= 0.1f;
            return waitTime <= 0;
        }

        public WaitForSeconds(float time)
        {
            waitTime = time;
        }
    }
}

waitTime -= 0.1f se determina según el número de ejecuciones por segundo del temporizador de la clase CoroutineLibrary, porque el temporizador que escribí se ejecuta una vez cada 100 milisegundos, luego se ejecutará 10 veces en 1 segundo, y cada vez es igual a 0.1, 10 veces Exactamente 1.

Agregar una clase CoroutineLibrary

using System.Collections;
using System.Collections.Generic;

namespace CoroutineLibrary
{
    public class CoroutineCSharp
    {
        /// <summary>
        /// 存储所有协程对象
        /// </summary>
        private static LinkedList<Coroutine> CoroutineList = new LinkedList<Coroutine>();
        /// <summary>
        /// 需要停止的协程
        /// </summary>
        private static Coroutine StopCoroutine = null;
        /// <summary>
        /// 定时器
        /// </summary>
        private static System.Timers.Timer Timer1 = null;

        /// <summary>
        /// 初始化
        /// </summary>
        private static void Init()
        {
            Timer1 = new System.Timers.Timer();
            Timer1.Interval = 100;
            Timer1.AutoReset = true;
            Timer1.Elapsed += Timer1_Elapsed;
            Timer1.Enabled = true;
        }

        private static void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            UpdateCoroutine();
        }


        /// <summary>
        /// 开启一个协程
        /// </summary>
        /// <param name="ie"></param>
        /// <returns></returns>
        public static Coroutine Start(IEnumerator ie)
        {
            if(Timer1 == null) Init();

            var c = new Coroutine(ie);
            CoroutineList.AddLast(c);
            return c;
        }

        /// <summary>
        /// 停止一个协程
        /// </summary>
        /// <param name="coroutine"></param>
        public static void Stop(Coroutine coroutine)
        {
            StopCoroutine = coroutine;
        }

        private static void UpdateCoroutine()
        {
            var node = CoroutineList.First;
            while (node != null)
            {
                bool ret = false;
                var cor = node.Value;
                if (cor != null)
                {
                    bool toStop = StopCoroutine == cor;
                    if (!toStop)
                        ret = cor.Next();
                }
                if (!ret)
                {
                    CoroutineList.Remove(node);
                }
                node = node.Next;
            }
        }


        private CoroutineCSharp() { }

        ~CoroutineCSharp()
        {
            Timer1.Enabled = false;
        }
    }
}

Al agregar tareas, los iteradores se almacenarán en una lista vinculada y luego el temporizador los actualizará repetidamente para determinar si estos iteradores han alcanzado el tiempo especificado y si deben detener la ejecución.

Eso es todo para todos los códigos sobre la simulación de rutinas, probemos algunas funciones básicas.

3. prueba

Cree un nuevo proyecto winform, agregue la biblioteca de clases anterior CoroutineLibrary y agregue dos botones.

El código de Form1 es el siguiente:

using CoroutineLibrary;
using System;
using System.Collections;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 模拟协程
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        Coroutine coroutine = null;

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            coroutine = CoroutineCSharp.Start(test1());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            CoroutineCSharp.Stop(coroutine);
        }

        IEnumerator test1()
        {
            while (true)
            {
                yield return new WaitForSeconds(1);
                Console.WriteLine("定时器1");
            }
        }

        IEnumerator test2()
        {
            Console.WriteLine("2开始");
            yield return new WaitForSeconds(2);
            Console.WriteLine("2结束1");
            yield return new WaitForSeconds(2);
            Console.WriteLine("2结束2");

            Console.WriteLine("2完成");
        }
    }
}

correr:

El primer método test1, el método test1 es un bucle while, por lo que se generará una vez cada segundo, y el segundo método test2 se generará varias veces, después de que se complete la ejecución, este método no se ejecutará nuevamente.

Probemos varias clases para ejecutar rutinas al mismo tiempo y cancelemos dos de ellas para ver el efecto.

Primero agregue una clase Test1

public class Test1
{
    private string Name { get; set; }
    public bool Switchs { get; set; }

    private Coroutine Coroutines { get; set; }

    public void Start()
    {
        Coroutines = CoroutineCSharp.Start(StartYield());
    }

    public void Stop()
    {
        CoroutineCSharp.Stop(Coroutines);
    }

    IEnumerator StartYield()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            Console.WriteLine("定时器,Name:{0}", Name);

            if (Switchs)
                break;
        }
    }

    public Test1(string name)
    {
        this.Name = name;
    }
}

Código Form1 para hacer algunos cambios.

using CoroutineLibrary;
using System;
using System.Collections;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 模拟协程
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        Test1 tests1 = new Test1("a1");
        Test1 tests2 = new Test1("a2");
        Test1 tests3 = new Test1("a3");
        Test1 tests4 = new Test1("a4");

        private void button5_Click(object sender, EventArgs e)
        {
            tests1.Start();
            tests2.Start();
            tests3.Start();
            tests4.Start();
        }

        private void button6_Click(object sender, EventArgs e)
        {
            tests2.Switchs = true;
            tests3.Switchs = true;
        }
    }
}

Iniciar varias tareas

Cancelar dos de las tareas

Finalizar

Si esta publicación es útil para usted, preste atención, dé me gusta y deje un mensaje.

fin

Supongo que te gusta

Origin blog.csdn.net/qq_38693757/article/details/132017693
Recomendado
Clasificación