我有一个这样的脚本:
public class Example : Monobehaviour
{
public async void Start()
{
LibraryClass service = new LibraryClass();
bool result = await service.Login();
if (result == false)
{
Debug.LogError("Login Failed !!");
return;
}
result = await service.Load();
if (result == false)
{
Debug.LogError("Load Failed !!");
return;
}
}
}
LibraryClass 方法的签名类似于:
public async Task<bool> Login();
和
public async Task<bool> Load();
运行脚本时,Unity主线程被阻塞,需要等待很长时间才能完成所有操作。 我决定添加一个进度条,但是,当然,直到这两个方法执行结束时才会更新任何内容。 我决定将脚本更改为如下所示:
public class Example : Monobehaviour
{
private IEnumerator Start()
{
LibraryClass service = new LibraryClass();
WaitForEndOfFrame wait = new WaitForEndOfFrame();
Task<bool> task = service.Login();
while (task.IsCompleted == false)
{
yield return wait;
}
if (task.Result == false)
{
Debug.LogError("Login Failed !!");
return;
}
task = service.Load();
while (task.IsCompleted == false)
{
yield return wait;
}
if (task.Result == false)
{
Debug.LogError("Load Failed !!");
return;
}
}
}
我注意到了一个改进,但主线程仍然会被阻塞并停止 UI 和游戏其余部分的更新,因为我认为该库内部在不同的操作上使用了几种不同的等待(HTTP 请求、加载 zip 文件内容)等...)我无法控制,因为是由我正在使用的这 2 个公共方法触发的。
有没有办法避免Unity主线程完全阻塞?
好吧,大家,这是我的解决方案:我将任务封装在线程中,我运行线程并等待线程。 该解决方案可带来丝般流畅的性能,甚至不会出现卡顿现象。 这是更改后的示例代码:
public class Example : Monobehaviour
{
private bool _threadRunning = false;
private IEnumerator Start()
{
LibraryClass service = new LibraryClass();
WaitForEndOfFrame wait = new WaitForEndOfFrame();
Thread thread = new Thread(() => { LoginThreadWorker(service); });
thread.Start();
_threadRunning = true;
while (_threadRunning)
{
yield return wait;
}
thread = new Thread(() => { LoginThreadWorker(service); });
thread.Start();
_threadRunning = true;
while (_threadRunning)
{
yield return wait;
}
}
private void LoginThreadWorker(LibraryClass service)
{
Task<bool> task = service.Login();
task.Wait();
if (task.Result == false)
{
Debug.LogError("Login Failed !!");
}
else
{
// TODO - successfully logged in.
}
_threadRunning = false;
}
private void LoadThreadWorker(LibraryClass service)
{
Task<bool> task = service.Load();
task.Wait();
if (task.Result == false)
{
Debug.LogError("Load Failed !!");
}
else
{
// TODO - successfully loaded.
}
_threadRunning = false;
}
}
因此,基本上,阻塞 Unity 主线程的 System.Threading.Task 现在被封装在封装在协程中的 System.Threading.Thread 中。 也许这不是最优雅的解决方案,但它对我有用,所以,我决定也在这里分享。
请注意:这仅有效,因为我从库中获得的System.Threading.Task方法不需要直接更改任何UnityEngine对象。如果您的任务正在与引擎中的任何内容进行交互,那么这种操作不适合您,因为您无法从 Unity 主线程外部更改 UnityEngine 对象。