Unity中如何按顺序异步执行带OnComplete回调的DoWork方法?
Unity中顺序异步执行Person.DoWork的解决方案
一、不修改原DoWork方法的原生实现(无.NET Task)
Unity自带的协程(Coroutine)是最适合的方案,完全在主线程运行,无需修改原有DoWork方法,也不会出现冻结问题:
// 顺序执行所有Person的DoWork,靠协程+回调控制流程 public IEnumerator ExecuteAllPersonsWorkSequentially() { foreach (var person in personList) { bool workCompleted = false; // 调用原DoWork,传入回调标记完成状态 person.DoWork(() => { workCompleted = true; }); // 每一帧检查完成状态,不会阻塞主线程 while (!workCompleted) { yield return null; } } } // 启动协程的方式(在MonoBehaviour类中调用) StartCoroutine(ExecuteAllPersonsWorkSequentially());
核心逻辑:用yield return null让协程暂停到下一帧再检查状态,避免了轮询占用CPU的问题,完全贴合Unity的帧循环机制。
二、若改用Task方案(解决你之前的冻结问题)
你之前的代码冻结,本质是用while (!allowExit) { await Task.Yield(); }的轮询方式低效且可能导致主线程调度异常。改用TaskCompletionSource包装回调,彻底摆脱轮询:
修改DoWork方法(兼容主线程)
public Task DoWork() { var taskSource = new TaskCompletionSource<bool>(); // 利用tool.Start的回调触发Task完成 tool.Start(() => { taskSource.SetResult(true); }); return taskSource.Task; } // 顺序执行的调用方法 public async Task RunAllPersonsWork() { foreach (var person in personList) { // ConfigureAwait(true)确保回到Unity主线程执行后续逻辑 await person.DoWork().ConfigureAwait(true); } } // 启动方式(在MonoBehaviour中) private async void Start() { await RunAllPersonsWork(); }
这种方式没有轮询,只有当tool.Start的回调触发时才会继续执行下一个任务,CPU占用极低,也不会出现冻结。
三、你之前代码冻结的原因
while (!allowExit) { await Task.Yield(); }的轮询方式会频繁将任务重新加入主线程调度队列,即使allowExit被触发,也可能因为调度延迟导致短时间内的CPU占用过高;而协程的yield return null是严格按Unity帧循环暂停,每一帧只检查一次状态,不会出现这类问题。
内容的提问来源于stack exchange,提问作者solidcomer
相关产品推荐
相关产品推荐

