下载图像时Async/await死锁

本文关键字:await 死锁 Async 图像 下载 | 更新日期: 2023-09-27 18:09:47

我正在开发一个Windows Phone 8.1应用程序。我有一个屏幕与缩略图的新闻标题列表。

首先,我正在制作异步http请求以JSON(满足NotifyTaskCompletion模式)获取新闻集合

NewsCategories = new NotifyTaskCompletion<ObservableCollection<NewsCategory>>(_newsService.GetNewsCategoriesAsync());

NewsCategory:

public class NewsCategory : ObservableObject
{
    ...
    public string Title { get;  set; }
    public ObservableCollection<News> Items { get;  set; }
}

新闻:

public class News : ObservableObject
{
    ...
    public string Title { get; set; }
    public string ImagePath { get; set; }
}

到目前为止,它工作完美,但一旦我得到ImagePath属性,我想下载并显示给定的图像。我已经找到了一个解决方案,在这里异步地做到这一点:WP 8.1从http请求绑定图像-以便当xaml获得图像路径时,它调用转换器类(BinaryToImageSourceConverter),也使用NotifyTaskCompletion模式。

出现问题的方法如下:

private async Task<BitmapImage> GetImage(string path)
{
    HttpClient webCLient = new HttpClient();
    var responseStream = await webCLient.GetStreamAsync(path);
    var memoryStream = new MemoryStream();
    await responseStream.CopyToAsync(memoryStream);
    memoryStream.Position = 0;
    var bitmap = new BitmapImage();
    await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
    return bitmap;
}

当第一个await被调用时,调试器永远不会到达下一行并且该方法永远不会返回。string path变量有合适的内容

到目前为止,我已经尝试使用ConfigureAwait(false),但它不工作在我的情况下。

我也在主题中发现:使用异步等待时死锁,即:

当你使用ConfigureAwait(false)时,你告诉你的程序你不介意上下文。它可以解决一些死锁问题,但通常不是正确的解决方案。正确的解决方案很可能是永远不要以阻塞的方式等待任务,并且始终保持异步。

我不知道在哪里可以用阻塞的方式放置这些东西。造成僵局的原因是什么?

如果这都是关于错误的方法,你知道什么模式更适合下载缩略图到一个项目的集合吗?

谢谢你的帮助。


更新:如何调用GetImage:它就像在主题:WP 8.1从http请求绑定图像

public class WebPathToImage : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value == null) return null;
        return new NotifyTaskCompletion<BitmapImage>(GetImage((String)value));
    }
    public object ConvertBack(object value, Type targetType, object parameter, string language)
    { throw new NotImplementedException(); }
    private async Task<BitmapImage> GetImage(string path)
    {
        using (var webCLient = new HttpClient())
        {
            webCLient.DefaultRequestHeaders.Add("User-Agent", "bot");
            var responseStream =  await webCLient.GetStreamAsync(path).ConfigureAwait(false);
            var memoryStream = new MemoryStream();
            await responseStream.CopyToAsync(memoryStream);
            memoryStream.Position = 0;
            var bitmap = new BitmapImage();
            await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
            return bitmap;  
        }
    }
}

and

<Image 
DataContext="{Binding ImagePath, Converter={StaticResource WebPathToImage}}"
Source="{Binding Result}" 
Stretch="UniformToFill" 
Height="79" Width="79"/>

下载图像时Async/await死锁

首先,如果你是在Windows Phone 8.1上,那么建议从命名空间Windows.Web.Http而不是从System.Net.Http中使用HttpClient。这个链接解释了一些原因。

我的答案使用新的Windows.Web.Http.HttpClient,所以你的GetImage方法可以看起来像这样:

        private async Task<BitmapImage> GetImage(string path)
        {
            using (var webCLient = new Windows.Web.Http.HttpClient())
            {
                webCLient.DefaultRequestHeaders.Add("User-Agent", "bot");
                var responseStream = await webCLient.GetBufferAsync(new Uri(path));
                var memoryStream = new MemoryStream(responseStream.ToArray());
                memoryStream.Position = 0;
                var bitmap = new BitmapImage();
                await bitmap.SetSourceAsync(memoryStream.AsRandomAccessStream());
                return bitmap;
            }
        }

我已经在一个示例URL上测试了这个方法,它在Image控件中正确显示。

第二,为什么不让Image控件在没有转换器的情况下处理下载BitmapImage,这样您的XAML看起来像这样:

<Image
    Source="{Binding ImagePath}" 
    Stretch="UniformToFill" 
    Height="79" 
    Width="79" />

这样你的ImagePath可以是一个路径从互联网和本地资源。

尝试在task . factory . run中调用GetImage来强制任务在另一个线程中执行。

注意你的位图创建,你可能会有问题,因为位图不是在UI线程中创建的。