完成任务后如何在Unity主线程上执行代码?

问题描述 投票:0回答:2

我目前正在开发一个Unity项目,我需要使用任务异步执行某些操作,然后在主线程上应用结果(例如更新UI或修改游戏对象)。虽然我知道 Unity 的主线程对于此类操作至关重要,以避免运行时错误,但我在确保代码执行完成任务后返回主线程方面遇到了困难。

我已经尝试了几种方法来解决这个问题,包括:

协程:尝试从任务的延续中启动协程,但协程似乎没有按预期方式执行,或者没有按照我的预期转换回主线程。

自定义调度程序类:创建的类类似于通常称为 UnityMainThreadDispatcher、Dispatcher 和 MainThreadExecutor 的类,这些类应该将更新循环期间在主线程上执行的操作排队。然而,这些解决方案对于我的需求来说并不可靠,要么是因为根本没有执行,要么是其他同步问题。

这是我想要实现的目标的简化版本:

Task.Run(() =>
{
   // Perform some asynchronous operation
   Debug.Log("Perform some asynchronous operation");
})
.ContinueWith(t => StartCoroutine(ExecuteOnMainThread()));
private IEnumerator ExecuteOnMainThread()
{
   // Code that needs to run on the main thread, e.g., UI updates
   Debug.Log("Code that needs to run on the main thread, e.g., UI updates");
   yield return null;
}

如果我执行此代码,只会弹出“执行某些异步操作”,它永远不会显示“需要在主线程上运行的代码,例如 UI 更新”。

我在网上看到了各种建议和代码片段,但没有一个在我的场景中完美运行。我正在联系,看看是否有人成功地从 Unity 中的任务中在主线程上执行代码,并且愿意分享他们的方法或见解。

具体来说,我正在寻找一个解决方案:

在不同的 Unity 版本之间可靠(我目前使用的是 Unity 2023.2.6f1 LTS)。 适用于涉及大量异步操作的任务,包括 Web 请求和数据处理。 能够处理任务中可能出现的异常,保证主线程仍然能够安全执行。 如果您遇到过类似的挑战并找到了可行的解决方案,或者您对改进我尝试过的方法有任何建议,我将非常感谢您的建议。

提前感谢您的帮助!

multithreading unity-game-engine
2个回答
1
投票

这个问题可以通过多种方式解决,但我将分享我最喜欢的在网络库中广泛使用的问题。 创建一个操作队列(如果您想让它更干净,您可以创建一个调用这些操作的对象队列),在需要更新 UI 时添加一个操作,并在 Unity 更新/后期更新中检查新操作。当您在队列中找到任何元素时,执行它们并将它们从队列中删除。 这可确保所有执行调用始终在 Unity 线程上,因此您可以将 Unity 组件修改为 UI 元素,同时保留在 Unity 线程外部修改队列的能力。


0
投票

感谢@adrian-sgro 的回复,这帮助我弄清楚如何解决我的问题。我想分享我在任务中在 Unity 主线程上执行代码的任务的更新。在尝试了各种方法(包括像 UnityMainThreadDispatcher 这样的自定义调度程序类)之后,我找到了适合我的特定情况的解决方案。

最初,我的尝试涉及创建一个单独的类来管理需要在主线程上执行的操作(Action)队列。尽管确保此类已附加到活动的 GameObject 并且启用了脚本,但 Unity 并未按预期调用此调度程序类中的 Update 方法。

这是我解决问题的方法:

我将操作队列直接移至主类中,其中已经有其他特定于 Unity 的逻辑(例如处理用户输入或更新游戏状态)。这是我所做的简化结构:

声明操作队列: 在我的主要 MonoBehaviour 类中,我声明了一个私有静态操作队列。

private static Queue<Action> actions = new Queue<Action>();

将任务中的操作入队: 每当我需要在主线程上执行某些任务作为任务完成的结果时,我都会像这样将操作排入队列:

actions.Enqueue(() =>
{
    Debug.Log("Test Enqueue from Task");
});

在Update中处理队列: 我在同一个 MonoBehaviour 类的 Update 方法中添加了一个循环来处理和执行操作。

while (actions.Count > 0)
{
    Debug.Log("Update called with " + actions.Count + " actions in queue");
    Action action = null;
    lock (actions)
    {
        if (actions.Count > 0)
        {
            action = actions.Dequeue();
        }
    }
    action?.Invoke();
}

结论:

似乎由于某种原因,单独的类已正确初始化,但 Unity 并未调用其 Update 方法。通过将操作队列直接集成到我的主 MonoBehaviour 类中,我确保从任务排队的操作按预期在主线程上执行。

这种方法可能并不适合所有场景,但它解决了我在异步操作之后在主线程上执行代码的具体问题。

我希望这个解决方案可以帮助其他面临类似挑战的人。如果有人知道为什么在单独的类场景中可能没有调用 Update 方法,我洗耳恭听!

© www.soinside.com 2019 - 2024. All rights reserved.