【问题标题】:How to properly wait for an unawaitable operation to complete如何正确等待无法等待的操作完成
【发布时间】:2014-07-28 05:54:25
【问题描述】:

在我的应用程序中,我创建了一个类 SerializableBitmapImage,它显示默认图像,而另一个图像从 Internet 下载。它还允许将下载的图像序列化为字节数组,因此不需要在每次应用启动时都下载图像。 我目前在“卡片”类中有一个 SerializableBitmapImage,它在卡片数据完成下载时立即保存:

var resp = await AppData.API.GetCards();
if (resp != null)
{
    if (AppData.CardsData == null)
        AppData.CardsData = new CardsData();
    AppData.CardsData.Update(resp);
    AppData.SaveCards();
    FillCards();
}

但是,有时 SerializableBitmapImage 中的图像在调用 AppData.SaveCards() 时还没有完成下载,导致图像无法保存。 所以,我正在寻找一种方法来延迟保存,直到图像被下载。现在,我在 SerializableBitmapImage 的构造函数中下载图像,如下所示:

Image = new BitmapImage(defaultUri); //set to default while downloading
BitmapImage img = new BitmapImage { CreateOptions = BitmapCreateOptions.None };
img.ImageOpened += (sender, e) => //download successful, set Image to downloaded image
{
    Image = sender as BitmapImage;
    NotifyPropertyChanged("Image");
};
img.UriSource = downloadUri; //download the image

我在 SerializableBitmapImage 中创建了一个任务来等待图像完成,但我不确定它是否以健康的方式完成了它的工作。

public async Task WaitForImageDownload()
{
    while (Image == null || Image.UriSource.OriginalString == "Assets/MainPage/DefaultCard.png")
        await Task.Delay(100); //check again if completed in 100ms
}

然后,在 AppData.SaveCards() 中,我在保存卡片之前等待所有图像下载完毕:

Task[] tasks = new Task[CardsData.Count];
for (int i = 0; i < CardsData.Count; i++)
    tasks[i] = CardsData[i].CardImage.WaitForImageDownload();
await Task.WhenAll(tasks);

这是等待无法等待完成的操作的正确方法吗?或者,有没有办法强制图像立即下载? 谢谢你的帮助! 艾略特

【问题讨论】:

  • 快速建议,可以等待的方法应以Async 后缀结尾,即public async Task WaitForImageDownloadAsync()

标签: c# asynchronous async-await bitmapimage


【解决方案1】:

你已经接近了。当然,每次你忙着等待的时候都应该响起警钟:

while (Image == null || Image.UriSource.OriginalString == "Assets/MainPage/DefaultCard.png")
    await Task.Delay(100); //check again if completed in 100ms

这并不完美。这将起作用,但是您不必要地启动了 CPU,并且您引入了可能被用户感知的延迟。最好只唤醒一个你正在等待的条件是真的。让图片通知您:

public async Task WaitForImageDownload()
{
    var tcs = new TaskCompletionSource<bool>();

    image.ImageOpened += (o, e) =>;
    {
        tcs.SetResult(true);
    };

    image.ImageFailed += (o, e) =>;
    {
        tcs.SetResult(false);
    };

    //set the source after the events have been attached
    //or, check whether the image has been loaded already and only
    //attach if it has
    image.SetSource(TODO); 

    await tcs.Task;

}

(最好将等待移到辅助方法中。)

您使用Task.WhenAll 是件好事。

或者,有没有办法强制立即下载图片?

从字面上回答这个问题:当然不是,那会违反物理学。 (我不知道你这个问题是什么意思......)

【讨论】:

  • 我什至不知道这个类的存在。但现在我愿意 :) 谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 2016-04-10
  • 1970-01-01
  • 2014-11-15
  • 1970-01-01
  • 2013-04-09
  • 1970-01-01
  • 2019-01-30
  • 2014-12-22
相关资源
最近更新 更多