以下函数偶尔会起作用,但大多数时候会阻塞await dlScreenshot_task
处的线程。
Debug.Log("Downloading from Storage...\n" + url);
触发每个图片网址,然后Unity死锁。偶尔它会抛出一个TaskCanceledException
。如果任务以任何方式失败,则return null
应该Task
。
public async Task<Texture2D> DownloadScreenshotFromStorageAsync(string url)
{
byte[] screenshotBytes;
//Firebase.Storage
StorageReference cloud = _firebaseStorage.GetReferenceFromUrl(_storageBucketURL + url);
if( cloud != null ) {
Task<byte[]> dlScreenshot_task = cloud.GetBytesAsync(1920 * 1080);
Debug.Log("Downloading from Storage...\n" + url);
try {
screenshotBytes = await dlScreenshot_task;
if( screenshotBytes != null ) {
Debug.Log("DLed: " + url);
return MetaLoader.DeserializeImage(screenshotBytes);
}
else {
return null;
}
}
catch(AggregateException aex ) {
StorageException store_ex = aex.InnerException as StorageException;
if(store_ex != null ) { Debug.Log("Storage Exception " + store_ex.ErrorCode); }
return null;
}
catch(TaskCanceledException t ) {
Debug.LogWarning(t);
return null;
}
}
else {
Debug.Log("Cloud Storage Reference failed for " + _storageBucketURL + url);
return null;
}
}
调用函数,使用MSDN Docs regarding Task-based async programming中的交错:
private async void DisplayScreenshotsFromDB()
{
Task<List<string>> dl_task = DownloadURLsAsync();
List<string> screenshotURLs = await dl_task;
//Interleaved image downloading
//https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
List<Task<Texture2D>> imageTasks =
(from imageUrl in screenshotURLs select DownloadScreenshotFromStorageAsync(imageUrl)).ToList();
while( imageTasks.Count > 0 ) {
Task<Texture2D> imageTask = await Task.WhenAny(imageTasks);
try {
imageTasks.Remove(imageTask);
Texture2D image = await imageTask;
DisplayScreenshot(image);
}
catch (Exception e) {
imageTasks.Remove(imageTask);
Debug.Log("Download failed\n" + e);
}
}
}
我是否创造了一种我没有看到的竞争条件?
或者为什么抓住TaskCanceledException
会阻止其他任务完成?
(它应该是可恢复的例外, return null
是可以接受的。)
值得注意的是,我不相信问题在于交错,调用功能。如果调用函数的结构如下,则会出现同样的问题:
Task<List<string>> dl_task = DownloadURLsAsync();
List<string> screenshotURLs = await dl_task;
foreach(string s in screenshotURLs ) {
await DownloadScreenshotAsync(s);
}