You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中完成Task后如何在主线程执行代码?

Unity异步Task完成后可靠切换主线程的解决方案

你的代码失败的核心原因是ContinueWith默认在后台线程执行,而Unity的StartCoroutine必须由主线程的MonoBehaviour实例调用,后台线程调用该方法不会触发协程执行。以下是两种可靠的解决方案:

方案一:利用Unity同步上下文直接切换

Unity 2019及以上版本支持SynchronizationContext,可在异步任务完成后直接切换到主线程上下文执行代码:

using UnityEngine;
using System.Threading.Tasks;
using System.Threading;

public class AsyncMainThreadExample : MonoBehaviour
{
    private void Start()
    {
        // 保存主线程的同步上下文
        var mainContext = SynchronizationContext.Current;

        Task.Run(() =>
        {
            // 执行耗时异步操作:网络请求、数据处理等
            Debug.Log($"异步操作线程ID:{Thread.CurrentThread.ManagedThreadId}");
        })
        .ContinueWith(t =>
        {
            // 处理异步任务的异常
            if (t.Exception != null)
            {
                Debug.LogError($"异步任务出错:{t.Exception.Flatten()}");
                return;
            }

            // 通过同步上下文切换到主线程
            mainContext.Post(_ =>
            {
                Debug.Log($"主线程ID:{Thread.CurrentThread.ManagedThreadId}");
                // 这里执行UI更新、游戏对象修改等主线程操作
                UpdateUI();
            }, null);
        });
    }

    private void UpdateUI()
    {
        // 示例:修改UI文本或游戏对象状态
        // scoreText.text = "任务完成";
    }
}

方案二:可靠的单例主线程调度器

如果需要在多个场景或脚本中复用主线程切换逻辑,可实现一个线程安全的单例调度器,通过Update循环执行队列中的主线程动作:

调度器实现代码

using UnityEngine;
using System.Collections.Generic;
using System.Threading;

public class MainThreadDispatcher : MonoBehaviour
{
    private static MainThreadDispatcher _instance;
    private static readonly Queue<System.Action> _actionQueue = new Queue<System.Action>();
    private static readonly object _lockObj = new object();

    public static MainThreadDispatcher Instance
    {
        get
        {
            if (_instance == null)
            {
                var dispatcherObj = new GameObject("MainThreadDispatcher");
                _instance = dispatcherObj.AddComponent<MainThreadDispatcher>();
                DontDestroyOnLoad(dispatcherObj);
            }
            return _instance;
        }
    }

    private void Update()
    {
        lock (_lockObj)
        {
            while (_actionQueue.Count > 0)
            {
                var action = _actionQueue.Dequeue();
                try
                {
                    action.Invoke();
                }
                catch (System.Exception ex)
                {
                    Debug.LogError($"主线程执行动作出错:{ex}");
                }
            }
        }
    }

    /// <summary>
    /// 将动作入队到主线程执行
    /// </summary>
    public void Enqueue(System.Action action)
    {
        if (action == null) return;
        lock (_lockObj) _actionQueue.Enqueue(action);
    }

    /// <summary>
    /// 静态快捷方法,直接提交动作到主线程
    /// </summary>
    public static void RunOnMainThread(System.Action action)
    {
        Instance.Enqueue(action);
    }
}

调度器使用示例

using UnityEngine;
using System.Threading.Tasks;

public class AsyncTaskUsage : MonoBehaviour
{
    private void Start()
    {
        Task.Run(async () =>
        {
            try
            {
                // 模拟大型异步任务:比如发起网络请求并解析数据
                await Task.Delay(2000);
                Debug.Log($"异步任务完成,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}");

                // 提交主线程操作
                MainThreadDispatcher.RunOnMainThread(() =>
                {
                    Debug.Log($"主线程执行更新,线程ID:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
                    UpdateGameState();
                });
            }
            catch (System.Exception ex)
            {
                Debug.LogError($"异步任务异常:{ex}");
                // 主线程显示错误提示
                MainThreadDispatcher.RunOnMainThread(() =>
                {
                    // errorTipText.text = $"加载失败:{ex.Message}";
                });
            }
        });
    }

    private void UpdateGameState()
    {
        // 示例:修改游戏对象位置或UI状态
        // playerTransform.position = new Vector3(0, 1, 0);
    }
}

关键注意事项

  • 异常处理:异步任务中的异常必须在Task内部捕获,或在ContinueWith中通过t.Exception获取,避免未捕获异常导致程序崩溃。
  • 线程安全:调度器的队列操作必须加锁,防止多线程同时入队导致的队列损坏。
  • 避免主线程耗时操作:主线程仅用于执行UI更新、游戏对象状态修改等必须主线程的操作,耗时计算仍留在异步线程中。
  • 版本兼容:上述方案兼容Unity 2019+,你的2023.2.6f1 LTS版本完全适用。

内容的提问来源于stack exchange,提问作者Diogo Garcia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 14:15:32