using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Text;

public class NewBehaviourScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GetComponent<Button>().onClick.AddListener(OnClick);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(new Vector3(0, 0, 0.5f));
    }

    void OnClick()
    {
        Debug.Log("OnClick");
        ComputeAsync();
    }

    async void ComputeAsync()
    {
        await Task.Run(() =>
        {
            for (int i = 0; i < 100000000; i++) Math.Sqrt(i);
            Debug.Log("ComputeAsync over");
        });
    }
}

 以上是函数异步调用无返回值的情况

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Text;

public class NewBehaviourScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GetComponent<Button>().onClick.AddListener(OnClick);
    }

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(new Vector3(0, 0, 0.5f));
    }

    /// <summary>
    /// 使用await后必须添加async关键字
    /// </summary>
    async void OnClick()
    {
        Debug.Log("OnClick");
        // 异步计算,无返回结果,主线程无阻塞画面不卡顿
        //ComputeAsync();

        // 异步计算并返回结果,主线程无阻塞画面不卡顿
        // await后的语句会挂起等到异步方法完成后再执行
        //string rtn = await ComputeAsyncWithResult();
        //Debug.Log("-------异步计算结果-------" + rtn);

        // 并行多个异步计算并返回结果,主线程无阻塞画面不卡顿
        var task1 = ComputeAsyncWithResult();
        Debug.Log("-------并发异步执行1-------");
        var task2 = ComputeAsyncWithResult();
        Debug.Log("-------并发异步执行2-------");
        var rtn1 = await task1;
        Debug.Log("-------rtn1-------" + rtn1);
        var rtn2 = await task2;
        Debug.Log("-------rtn2-------" + rtn2);
    }

    async void ComputeAsync()
    {
        await Task.Run(() =>
        {
            for (int i = 0; i < 100000000; i++) Math.Sqrt(i);
            Debug.Log("ComputeAsync over");
        });
    }

    async Task<string> ComputeAsyncWithResult()
    {
        return await Task.Run(() =>
        {
            double sqrt = 0;
            for (int i = 0; i < 100000000; i++) sqrt = Math.Sqrt(i);
            return sqrt.ToString();
        });
    }
}

以上是函数异步调用有返回值的情况,更为通用

需要注意以下要点

1.函数中有await运算符时,函数名必须有async修饰,如代码中的按钮点击回调函数返回值为void可以直接加上async

2.async修饰后的函数返回类型必须为

具体Task<TResult>类型时,返回值需要是TResult类型

3.await 运算符暂停对其所属的 await 方法的求值,直到其操作数表示的异步操作完成。

也就是说,一般的,await Task<TResult> 将得到TResult类型的值,并产生一段时间的挂起

4.Task.Run(func/lambda)

Task是.NET4.0加入的,跟线程池ThreadPool的功能类似,用Task开启新任务时,会从线程池中调用线程,而Thread每次实例化都会创建一个新的线程。

Task.Run(func/lambda)会在子线程执行func/lambda

并根据func/lambda的返回类型而自动返回一个Task<TResult>

若有多个子线程计算需要同步进行,可以连续执行多个Task.Run在对其返回的Task对象做await

而不要每执行一个Task.Run就立即await(await会挂起待异步函数执行完毕)

5.Unity中诸如寻路算法等计算耗时高的函数,可以用这种异步方式处理,有延时但无画面卡顿

 

 

 参考资料

https://docs.microsoft.com/zh-CN/dotnet/csharp/language-reference/keywords/async

https://docs.microsoft.com/zh-CN/dotnet/csharp/language-reference/operators/await

https://docs.microsoft.com/zh-CN/dotnet/csharp/programming-guide/concepts/async/