完整教程:u3d跨线程UI通知的实现原理和使用方法

完整的实现步骤

1. 首先创建主线程调度器(必需)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static readonly Queue _executionQueue = new Queue();
private static UnityMainThreadDispatcher _instance = null;
public static UnityMainThreadDispatcher Instance()
{
if (!Exists())
{
// 如果没有实例,创建一个
GameObject go = new GameObject("MainThreadDispatcher");
_instance = go.AddComponent();
DontDestroyOnLoad(go);
}
return _instance;
}
public static bool Exists()
{
return _instance != null;
}
public void Enqueue(Action action)
{
lock(_executionQueue)
{
_executionQueue.Enqueue(action);
}
}
void Update()
{
lock(_executionQueue)
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
}
void Awake()
{
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
else if (_instance != this)
{
Destroy(gameObject);
}
}
void OnDestroy()
{
_instance = null;
}
}

2. 创建事件管理器

using System;
using UnityEngine;
public static class ThreadEventManager
{
// 定义事件
public static event Action OnThreadCompleted;
public static event Action OnProgressUpdated;
// 线程安全的事件触发方法
public static void TriggerThreadCompleted(string message)
{
// 将UI更新操作调度到主线程
UnityMainThreadDispatcher.Instance().Enqueue(() => {
OnThreadCompleted?.Invoke(message);
});
}
public static void TriggerProgressUpdated(int progress)
{
UnityMainThreadDispatcher.Instance().Enqueue(() => {
OnProgressUpdated?.Invoke(progress);
});
}
}

3. 创建执行后台线程的类

using System.Threading;
using UnityEngine;
public class BackgroundTaskRunner : MonoBehaviour
{
void Start()
{
// 启动后台任务
StartBackgroundTask();
}
void StartBackgroundTask()
{
Thread backgroundThread = new Thread(DoBackgroundWork);
backgroundThread.Start();
}
// 这个方法在后台线程中运行
void DoBackgroundWork()
{
Debug.Log("后台任务开始执行...");
// 模拟耗时任务,分步骤执行
for (int i = 0; i <= 100; i += 10)
{
Thread.Sleep(500); // 模拟工作耗时
// 通知UI更新进度(从后台线程调用)
ThreadEventManager.TriggerProgressUpdated(i);
Debug.Log($"后台线程: 进度 {i}%");
}
// 任务完成,通知UI
ThreadEventManager.TriggerThreadCompleted("后台任务已完成!");
Debug.Log("后台任务执行完毕");
}
}

4. UI界面控制器(你提供的代码)

using UnityEngine;
using UnityEngine.UI;
public class UIController : MonoBehaviour
{
[SerializeField] private Text statusText;
[SerializeField] private Slider progressSlider;
void Start()
{
// 订阅事件 - 当其他线程触发事件时,这些方法会被调用
ThreadEventManager.OnThreadCompleted += HandleThreadCompleted;
ThreadEventManager.OnProgressUpdated += HandleProgressUpdated;
Debug.Log("UI界面已订阅线程事件");
}
// 处理线程完成事件
void HandleThreadCompleted(string message)
{
statusText.text = message;
Debug.Log($"UI收到完成通知: {message}");
}
// 处理进度更新事件
void HandleProgressUpdated(int progress)
{
progressSlider.value = progress / 100f; // Slider的值通常是0-1
Debug.Log($"UI更新进度: {progress}%");
}
void OnDestroy()
{
// 重要:取消订阅,防止内存泄漏
ThreadEventManager.OnThreadCompleted -= HandleThreadCompleted;
ThreadEventManager.OnProgressUpdated -= HandleProgressUpdated;
}
}

具体使用场景示例

场景1:网络请求完成后通知UI

public class NetworkManager : MonoBehaviour
{
public void DownloadData()
{
Thread downloadThread = new Thread(() => {
try
{
// 模拟网络下载
for (int i = 0; i <= 100; i += 5)
{
Thread.Sleep(100);
ThreadEventManager.TriggerProgressUpdated(i);
}
// 下载完成
ThreadEventManager.TriggerThreadCompleted("数据下载完成!");
}
catch (System.Exception ex)
{
ThreadEventManager.TriggerThreadCompleted($"下载失败: {ex.Message}");
}
});
downloadThread.Start();
}
}

场景2:文件处理完成后通知UI

public class FileProcessor : MonoBehaviour
{
public void ProcessLargeFile(string filePath)
{
Thread processingThread = new Thread(() => {
// 模拟文件处理
string[] steps = {"读取文件", "解析数据", "验证内容", "保存结果"};
for (int i = 0; i < steps.Length; i++)
{
Thread.Sleep(1000); // 模拟处理时间
int progress = (i + 1) * 25; // 25%, 50%, 75%, 100%
ThreadEventManager.TriggerProgressUpdated(progress);
// 如果是最后一步
if (i == steps.Length - 1)
{
ThreadEventManager.TriggerThreadCompleted("文件处理完成!");
}
}
});
processingThread.Start();
}
}

实现原理解析

  1. 事件订阅机制

    // UIController在Start()中订阅事件
    ThreadEventManager.OnThreadCompleted += HandleThreadCompleted;
  2. 跨线程安全调用

    // 后台线程通过事件管理器触发事件
    ThreadEventManager.TriggerThreadCompleted("消息");
    // 事件管理器确保UI更新在主线程执行
    UnityMainThreadDispatcher.Instance().Enqueue(() => {
    OnThreadCompleted?.Invoke(message);
    });
  3. 主线程调度

    // UnityMainThreadDispatcher在每帧Update中执行队列中的操作
    void Update()
    {
    while (_executionQueue.Count > 0)
    {
    _executionQueue.Dequeue().Invoke(); // 在主线程执行UI更新
    }
    }

关键要点

  1. 线程安全:所有UI操作必须在主线程中执行
  2. 事件解耦:发送方(后台线程)和接收方(UI)完全解耦
  3. 内存管理:记得在OnDestroy中取消事件订阅
  4. 异常处理:在后台线程中要处理可能的异常

这样,你就可以从任何后台线程安全地通知和更新Unity的UI界面了!

posted @ 2025-09-02 11:04  yfceshi  阅读(23)  评论(0)    收藏  举报