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/