使用 async-await 得到更简单的逻辑代码

  从API版本升级到4.6之后, Unity支持了async和await语法, 并且根据测试来看, 它运行在主线程里, 跟一般的C#编译不大一样, 这就很有操作空间了, 先来看看普通C# Console工程和Unity中运行的差别:

  1. C# Console

using System;

namespace AsyncTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Console.WriteLine("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);   // 1
            Test();

            Console.ReadLine();
        }

        async static void Test()
        {
            await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(5));
            Console.WriteLine("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);   // 4
        }
    }
}

  运行结果可以看到运行在不同的线程里面 : 

  2. Unity 

using UnityEngine;

public class AsyncAwaitTest : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);  // 1
        Test();
    }
    async static void Test()
    {
        await System.Threading.Tasks.Task.Delay(System.TimeSpan.FromSeconds(5));
        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);  // 1
    }
}

  运行结果可以看到运行在主线程里面 : 

  这样的好处是什么呢? 第一个是它跟协程一样了, 通过不同的await方法返回不同的对象实现协程的作用, 我发现它可以使用 WaitForSeconds 这些Unity自带的控制类型, 比较神奇, 看下面测试:

using UnityEngine;

public class AsyncAwaitTest : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time1 : " + Time.time);
        Debug.Log("Time2 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));
        Test();
    }
    async static void Test()
    {
        //await System.Threading.Tasks.Task.Delay(System.TimeSpan.FromSeconds(5));
        Time.timeScale = 2.0f;
        await new WaitForSeconds(2.0f);

        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time3 : " + Time.time);
        Debug.Log("Time4 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));       
    }
}

  运行结果如下:

  上面的运行在开始时调整了Time.timeScale, 然后等待的时间 WaitForSeconds(2.0) 运行结果也是正确的, 看到游戏时间过了2秒, 实际时间过了1秒, 也就是说Unity中对await的返回进行了整合, 自带的YieldInstruction也能被await正确返回. 这样async方法就能直接当做协程来用了.

  测试一下多个async嵌套运行的情况:

using UnityEngine;

public class AsyncAwaitTest : MonoBehaviour
{
    void Start()
    {
        Debug.Log("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time1 : " + Time.time);
        Debug.Log("Time2 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));
        Test();
    }
    async void Test()
    {
        Time.timeScale = 2.0f;
        await new WaitForSeconds(2.0f);

        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time3 : " + Time.time);
        Debug.Log("Time4 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));

        await Test2();
    }
    async System.Threading.Tasks.Task Test2()
    {
        await new WaitForSecondsRealtime(2.0f); // Time.timeScale = 2.0f;
        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time5 : " + Time.time);
        Debug.Log("Time6 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));
    }
}

  运行结果 : 

  正确的结果, 因为在Test2中timeScale还是2, 使用realtime的话就是4秒的游戏时间. 

  都是在主线程中运行的, 这样看来因为async是语言级别的支持, 可能以后就没有协程什么事了, 使用async在写法上也比协程简单了一点, 我们试试用协程来写:

    void Start()
    {
        Debug.Log("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time1 : " + Time.time);
        Debug.Log("Time2 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));

        StartCoroutine(Test());
    }
    IEnumerator Test()
    {
        Time.timeScale = 2.0f;
        yield return new WaitForSeconds(2.0f);

        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time3 : " + Time.time);
        Debug.Log("Time4 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));

        yield return Test2();
    }
    IEnumerator Test2()
    {
        yield return new WaitForSecondsRealtime(2.0f); // Time.timeScale = 2.0f;
        Debug.Log("Async : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time5 : " + Time.time);
        Debug.Log("Time6 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));
    }

  差别在StartCoroutine上, 反正我是经常忘了写它, 然后运行不起来的. 因为没有什么好方法测试两种方案的性能差别, 暂时先抛开性能吧.

  然后是 WaitForEndOfFrame 在async是否正确的测试 : 

using UnityEngine;

public class AsyncAwaitTest : MonoBehaviour
{
    bool update = false;
    void Start()
    {
        Debug.Log("Main : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        Debug.Log("Time1 : " + Time.time);
        Debug.Log("Time2 : " + System.DateTime.Now.ToString("HH:mm:ss fff"));
        Test();
    }
    async void Test()
    {
        int i = 0;
        update = true;
        while(i < 10)
        {
            i++;
            Debug.Log("Async -- " + Time.frameCount);
            await new WaitForEndOfFrame();
        }
        update = false;
    }
    void Update()
    {
        if(update)
        {
            Debug.Log("Update -- " + Time.frameCount);
        }
    }
}

  可以看到跟Update函数是交互进行的, 确实async能以YieldInstruction作为等待逻辑.

猜你喜欢

转载自www.cnblogs.com/tiancaiwrk/p/12420902.html