Unity C# - 在协程中执行异步任务并等待它完成

Unity C# - Execute async task within a Coroutine and wait for it to finish

我是 Unity 开发的新手,我正在尝试将异步功能集成到现有的协程中,但到目前为止我遇到了几个问题。 我的问题:

  1. Unity 应用程序完全死机,可能是因为它被线程阻塞了。我在传统的 C#(控制台应用程序)中实现了这段代码,没有任何问题。(经过一些修改后现在在 Unity 中修复)
  2. 下载任务开始但从未完成。只有当我 运行 它作为 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 上尝试用作路径的内容;)