Unity Task 的用法

Unity 中使用 Task 似乎与在纯c#中使用Task 有所不同
Unity 主线程调用 async 方法时,不论是 await 之前还是之后,代码都在主线程运行
c# 主线程调用async 方法时,await之前由主线程运行,await之后由子线程运行

导致这个差异的原因是Unity 使用了 UnitySynchronizationContext 覆盖默认 SynchronizationContext 。
源码: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Scripting/UnitySynchronizationContext.cs

UnitySynchronizationContext 中 Get 以及 Post 都存储了方法到队列中,使用主线程来处理队列中的消息.

unity 

async void task(){
语句1  //主线程调用
await taskMethod; //这里肯定是新线程执行了  
语句2 //主线程调用
}

unity 这个改动的好处是
unity 中使用 async 来处理异步方法就不需要考虑线程安全问题了,可以代替协程。
相比于协程,Task 更加灵活,不需要 Monobehavior 来开启,可以进行错误捕获。

要注意的地方:
第一:
在主线程使用 task.wait 方法 或者 使用 task.Result 会导致死锁

看个例子:
在这里插入图片描述
上面的代码运行Unity 后 Unity 会卡死。原因是产生了死锁!
分析下原因。
taskValue.wait 会去等待task001 任务的执行完成。
await task() 运行完成之后, 上下文切换到主线程。
也就是说 Debug.Log(“task001 end!”); 这个是需要主线程来执行的, 然而主线现在正在忙着执行taskValue.Wait(),没有空执行 Debug.Log(“task001 end!”); 这就导致了 task001 在等主线程来执行, 主线忙着等待 task001()。导致主线程无法向下执行。

第二: 值类型在await 值类型的方法中,代码获得的是值类型的一个拷贝。
例子:

public struct taskTest
{
    public int x;

    public  async Task task()
    {
        x = 100;

        Debug.Log("Task " + Thread.CurrentThread.ManagedThreadId);
        await task01();
        Debug.Log("Task01:" + Thread.CurrentThread.ManagedThreadId + "," + x);
    }

    public  Task task01()
    {
        return Task.Factory.StartNew(
            () => Thread.Sleep(5000)
            );
    }
}

monobehavior 中
    void Start()
    {
        task();
    }

    async Task task()
    {

        var tt = new taskTest();

        await tt.task();

        Debug.Log("xx:" + tt.x);

    }

在这里插入图片描述
可以发现子线程执行的 taskTest.task 方法中的实例 与 主线程中的 taskTest 实例不是同一个, taskTest.task 获得的是一个新副本。 具体原因就要看Unity源代码了。

参考
https://zhuanlan.zhihu.com/p/86168785

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Unity中使用Task来实现异步操作时,可以使用Stopwatch类来计算线程执行时间。Stopwatch类是C#中的一个计时器类,可以精确地计算代码执行时间。 在异步任务中,可以在开始执行前调用Stopwatch.StartNew方法来启动计时器,在执行完成后调用Stopwatch.Stop方法来停止计时器,并使用Stopwatch.Elapsed属性来获取线程执行时间。最后将线程执行时间显示到UI界面上,或者通过Debug.Log方法输出到控制台上。 下面是一个简单的示例,使用Task和Stopwatch来实现异步任务,并显示线程执行时间: ``` async void MyFunction() { // 启动计时器 Stopwatch sw = Stopwatch.StartNew(); // 创建异步任务 Task myTask = Task.Factory.StartNew(MyThreadFunction); // 等待任务完成 await myTask; // 停止计时器 sw.Stop(); // 显示线程执行时间 Debug.Log("线程执行时间:" + sw.ElapsedMilliseconds + "ms"); } void MyThreadFunction() { // 模拟耗时操作 Thread.Sleep(3000); } ``` 在MyFunction函数中,使用Stopwatch类启动计时器,并创建了一个异步任务myTask。在等待任务完成后,停止计时器,并使用sw.ElapsedMilliseconds属性获取线程执行时间。最后使用Debug.Log方法将线程执行时间输出到控制台上。 需要注意的是,Stopwatch类的计时器精度是纳秒级别的,可以精确地计算代码执行时间。同时,需要确保异步任务不会访问主线程对象,否则可能会导致线程死锁。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值