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

如何在Unity3D中使用async-await?解决异步任务与UI交互等问题

Unity中Async-Await的正确使用方案

一、基础适配:让Async-Await在Unity中正常工作

Unity 2017及以上版本原生支持C#的async-await特性,但需注意两个核心前提:

  • 确保脚本继承自MonoBehaviour,且在Player Settings中设置脚本运行时版本为.NET 4.x+ Equivalent(新版Unity对应.NET Standard/.NET Framework)
  • async方法无需作为协程启动,直接调用即可,但必须做好异常捕获

二、网络操作(TcpClient/HttpClient)的正确实现

以HttpClient调用Web API为例,直接结合async-await即可避免协程的局限:

using System.Net.Http;
using UnityEngine;
using UnityEngine.UI;

public class NetworkHandler : MonoBehaviour
{
    public Text resultDisplay;
    private HttpClient _httpClient;

    void Start()
    {
        _httpClient = new HttpClient();
        // 直接触发异步任务
        FetchApiData();
    }

    async void FetchApiData()
    {
        try
        {
            string response = await _httpClient.GetStringAsync("https://your-api-endpoint.com/data");
            // 此处不能直接操作UI,需切换主线程
            UpdateUIFromMainThread(response);
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"请求失败: {ex.Message}");
        }
    }
}

三、解决Async任务中无法操作UI的问题

Unity UI组件仅允许在主线程修改,await后的代码可能运行在子线程,需通过以下方式切换回主线程:

1. 自定义主线程调度器(通用方案)

实现一个全局调度器,将UI操作加入主线程队列执行:

using System.Collections.Generic;
using UnityEngine;

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

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

    void Update()
    {
        lock (_pendingActions)
        {
            while (_pendingActions.Count > 0)
            {
                _pendingActions.Dequeue().Invoke();
            }
        }
    }

    public void QueueAction(System.Action action)
    {
        lock (_pendingActions)
        {
            _pendingActions.Enqueue(action);
        }
    }
}

调用方式:

async void FetchApiData()
{
    try
    {
        string response = await _httpClient.GetStringAsync("https://your-api-endpoint.com/data");
        MainThreadDispatcher.Instance.QueueAction(() => 
        {
            resultDisplay.text = response;
        });
    }
    catch (System.Exception ex)
    {
        Debug.LogError($"请求失败: {ex.Message}");
    }
}

2. 用UnityWebRequest结合Async-Await(原生方案)

Unity自带的UnityWebRequest与async-await结合时,回调默认在主线程执行,无需额外切换:

using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class UnityWebRequestWrapper : MonoBehaviour
{
    public Text resultDisplay;

    async void Start()
    {
        await FetchDataWithUnityWebRequest();
    }

    async System.Threading.Tasks.Task FetchDataWithUnityWebRequest()
    {
        using (var request = UnityWebRequest.Get("https://your-api-endpoint.com/data"))
        {
            var operation = request.SendWebRequest();
            // 等待请求完成,全程在主线程
            while (!operation.isDone)
            {
                await System.Threading.Tasks.Task.Yield();
            }

            if (request.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError(request.error);
            }
            else
            {
                // 直接操作UI
                resultDisplay.text = request.downloadHandler.text;
            }
        }
    }
}

3. 捕获主线程同步上下文(轻量方案)

通过捕获Unity的同步上下文,强制后续代码回到主线程:

async void FetchApiData()
{
    var mainThreadContext = System.Threading.SynchronizationContext.Current;
    try
    {
        string response = await _httpClient.GetStringAsync("https://your-api-endpoint.com/data");
        // 利用同步上下文切换回主线程
        mainThreadContext.Post(_ => 
        {
            resultDisplay.text = response;
        }, null);
    }
    catch (System.Exception ex)
    {
        Debug.LogError($"请求失败: {ex.Message}");
    }
}

四、Async-Await与Coroutines的配合

  • Async-await更适配C#标准异步API(如HttpClient、TcpClient),无需手动拆分回调逻辑
  • 两者可互相配合:在async方法中调用协程,或在协程中等待async任务:
IEnumerator CoroutineWaitForAsyncTask()
{
    var apiTask = _httpClient.GetStringAsync("https://your-api-endpoint.com/data");
    yield return new WaitUntil(() => apiTask.IsCompleted);
    
    if (apiTask.IsFaulted)
    {
        Debug.LogError(apiTask.Exception.Message);
    }
    else
    {
        resultDisplay.text = apiTask.Result;
    }
}

内容的提问来源于stack exchange,提问作者Junaid Pathan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 03:12:18