如何正确等待无法完成的操作

时间:2014-07-27 21:24:01

标签: c# asynchronous async-await bitmapimage

在我的应用程序中,我创建了一个类SerializableBitmapImage,它显示默认图像,而另一个图像从Internet下载。它还允许将下载的图像序列化为字节数组,因此不需要在每次应用程序启动时下载图像。 我目前在“Card”类中有一个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中创建了一个Task来等待图像完成,但我不确定它是否以健康的方式完成它的工作。

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);

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

1 个答案:

答案 0 :(得分:6)

你很亲密。当然,每次你忙着等待警报都应该响起:

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的使用是一件好事。

  

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

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