Unity C# - 在协程中执行异步任务并等待它完成
Unity C# - Execute async task within a Coroutine and wait for it to finish
我是 Unity 开发的新手,我正在尝试将异步功能集成到现有的协程中,但到目前为止我遇到了几个问题。
我的问题:
- Unity 应用程序完全死机,可能是因为它被线程阻塞了。我在传统的 C#(控制台应用程序)中实现了这段代码,没有任何问题。(经过一些修改后现在在 Unity 中修复)
- 下载任务开始但从未完成。只有当我 运行 它作为 APK 时才会发生这种情况。在 PC 上的 Unity 调试工作正常。
我的代码:
public void Validate()
{
StartCoroutine(DoWork());
}
private IEnumerator DoWork()
{
bool success;
//Perform some calculations here
..
..
success = true;
//
yield return new WaitForSeconds(3);
if(success){
GetInfo(_files);
}
}
async void GetInfo(List<string> files)
{
await StartDownload(files);
//After completing the download operation, perform some other actions in the background
...
...
//
//When done, change the active status of specific game objects
}
public async Task StartDownload(List<string> files){
var t1 = GetFileSizesAsync(files);
var t2 = DownloadFilesAsync(files);
await Task.WhenAll(t1, t2);
}
public async Task GetFileSizesAsync(List<string> urls)
{
foreach (var url in urls)
GetFileSize(url);
txtSize.text = totalSizeMb +"MB";
}
private void GetFileSize(string url)
{
var uri = new Uri(url);
var webRequest = HttpWebRequest.Create(uri);
webRequest.Method = "GET";
try
{
var webResponse = webRequest.GetResponse();
var fileSize = webResponse.Headers.Get("Content-Length");
var fileSizeInMegaByte = Math.Round(Convert.ToDouble(fileSize) / 1024.0 / 1024.0, 2);
totalSizeMb = totalSizeMb + fileSizeInMegaByte;
}
catch (Exception ex)
{
}
finally
{
webRequest.Abort();
}
}
public async Task<List<string>> DownloadFilesAsync(List<string> urls)
{
var result = new List<string>();
foreach (var url in urls)
var download = await DownloadFile(url);
if(download)
result.Add(url);
return response;
}
private async Task<bool> DownloadFile(string url)
{
WebClient webClient = new WebClient();
var uri = new Uri(url);
var saveHere = "C:\...";
try
{
await webClient.DownloadFileTaskAsync(uri, saveHere);
return true;
}
catch (Exception ex)
{
return false;
}
}
你能告诉我我做错了什么吗?我尝试了多种方法,但无法找到合适的解决方案。
谢谢!
首先。
冻结肯定是由 IEneumerator 完成的,因为我没有看到 yield 语句。什么是收益声明?呃……Google它。莱尔
其次。
普通的 void 不是很好吗?
第三。
我对异步不太了解,因为我对他们很陌生
但我相当确定您不需要它们:
任务 GetFileSizesAsync(列出 url)
和
async Task DownloadFilesAsync(列出 url)
我可能是错的。
我宁愿去
async Task GetInfo(List<string> files){ ... }
然后在你的协程中做
var t = Task.Run(async () => await GetInfo(files));
while(!t.IsCompleted)
{
yield return null;
}
// Or also
//yield return new WaitUntil(() => t.IsCompleted);
if(!t.IsCompletedSuccesfully)
{
Debug.LogError("Task failed or canceled!");
yield break;
}
但是请注意:
When done, change the active status of specific game objects
这不能异步完成!它必须发生在 Unity 主线程中!因此,您可能更愿意 return 从 GetInfo
任务中获取一些东西,并在完成后激活协程中的对象。
在循环和 yield 之后,您可以通过
访问 return 值
var result = t.Result;
您的网络请求目前完全多余!您开始获取请求只是为了检查接收到的内容有多大,但立即丢弃接收到的内容……然后您开始第二个请求以实际下载文件(再次)。
一般来说,我更推荐使用 UnityWebRequest.Get
you can directly yield in the Coroutine in combination with a DownloadHandlerFile
,它允许您直接将内容下载到本地文件而不是运行时内存。
还有
var saveHere = "C:\...";
希望不是您在 Android 上尝试用作路径的内容;)
我是 Unity 开发的新手,我正在尝试将异步功能集成到现有的协程中,但到目前为止我遇到了几个问题。 我的问题:
- Unity 应用程序完全死机,可能是因为它被线程阻塞了。我在传统的 C#(控制台应用程序)中实现了这段代码,没有任何问题。(经过一些修改后现在在 Unity 中修复)
- 下载任务开始但从未完成。只有当我 运行 它作为 APK 时才会发生这种情况。在 PC 上的 Unity 调试工作正常。
我的代码:
public void Validate()
{
StartCoroutine(DoWork());
}
private IEnumerator DoWork()
{
bool success;
//Perform some calculations here
..
..
success = true;
//
yield return new WaitForSeconds(3);
if(success){
GetInfo(_files);
}
}
async void GetInfo(List<string> files)
{
await StartDownload(files);
//After completing the download operation, perform some other actions in the background
...
...
//
//When done, change the active status of specific game objects
}
public async Task StartDownload(List<string> files){
var t1 = GetFileSizesAsync(files);
var t2 = DownloadFilesAsync(files);
await Task.WhenAll(t1, t2);
}
public async Task GetFileSizesAsync(List<string> urls)
{
foreach (var url in urls)
GetFileSize(url);
txtSize.text = totalSizeMb +"MB";
}
private void GetFileSize(string url)
{
var uri = new Uri(url);
var webRequest = HttpWebRequest.Create(uri);
webRequest.Method = "GET";
try
{
var webResponse = webRequest.GetResponse();
var fileSize = webResponse.Headers.Get("Content-Length");
var fileSizeInMegaByte = Math.Round(Convert.ToDouble(fileSize) / 1024.0 / 1024.0, 2);
totalSizeMb = totalSizeMb + fileSizeInMegaByte;
}
catch (Exception ex)
{
}
finally
{
webRequest.Abort();
}
}
public async Task<List<string>> DownloadFilesAsync(List<string> urls)
{
var result = new List<string>();
foreach (var url in urls)
var download = await DownloadFile(url);
if(download)
result.Add(url);
return response;
}
private async Task<bool> DownloadFile(string url)
{
WebClient webClient = new WebClient();
var uri = new Uri(url);
var saveHere = "C:\...";
try
{
await webClient.DownloadFileTaskAsync(uri, saveHere);
return true;
}
catch (Exception ex)
{
return false;
}
}
你能告诉我我做错了什么吗?我尝试了多种方法,但无法找到合适的解决方案。
谢谢!
首先。 冻结肯定是由 IEneumerator 完成的,因为我没有看到 yield 语句。什么是收益声明?呃……Google它。莱尔
其次。 普通的 void 不是很好吗?
第三。 我对异步不太了解,因为我对他们很陌生 但我相当确定您不需要它们:
任务 GetFileSizesAsync(列出 url)
和
async Task DownloadFilesAsync(列出 url)
我可能是错的。
我宁愿去
async Task GetInfo(List<string> files){ ... }
然后在你的协程中做
var t = Task.Run(async () => await GetInfo(files));
while(!t.IsCompleted)
{
yield return null;
}
// Or also
//yield return new WaitUntil(() => t.IsCompleted);
if(!t.IsCompletedSuccesfully)
{
Debug.LogError("Task failed or canceled!");
yield break;
}
但是请注意:
When done, change the active status of specific game objects
这不能异步完成!它必须发生在 Unity 主线程中!因此,您可能更愿意 return 从 GetInfo
任务中获取一些东西,并在完成后激活协程中的对象。
在循环和 yield 之后,您可以通过
访问 return 值var result = t.Result;
您的网络请求目前完全多余!您开始获取请求只是为了检查接收到的内容有多大,但立即丢弃接收到的内容……然后您开始第二个请求以实际下载文件(再次)。
一般来说,我更推荐使用 UnityWebRequest.Get
you can directly yield in the Coroutine in combination with a DownloadHandlerFile
,它允许您直接将内容下载到本地文件而不是运行时内存。
还有
var saveHere = "C:\...";
希望不是您在 Android 上尝试用作路径的内容;)