简体   繁体   English

下载图像时异步/等待死锁

[英]Async/await deadlock while downloading images

I'm developing a Windows Phone 8.1 app. 我正在开发一个Windows Phone 8.1应用程序。 I have a screen with a list of news' titles with thumbnails. 我有一个屏幕,上面有一些带有缩略图的新闻标题。

First I'm making asynchronous http request to get news collection in JSON (satisfying the NotifyTaskCompletion pattern ) 首先,我正在制作异步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; }
}

News: 新闻:

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

So far it works perfectly, but as soon as I get the ImagePath property, I would like to download and display the given image. 到目前为止它完美地工作,但是一旦我获得ImagePath属性,我想下载并显示给定的图像。 I've found a solution to do it asynchronously here: WP 8.1 Binding image from a http request - so that when xaml gets the image path, it calls a converter class ( BinaryToImageSourceConverter ), also using the NotifyTaskCompletion pattern. 我找到了一个在这里异步执行的解决方案: WP 8.1绑定来自http请求的图像 - 这样当xaml获取图像路径时,它也会使用NotifyTaskCompletion模式调用转换器类( BinaryToImageSourceConverter )。

The problem occurs in the following method: 问题出现在以下方法中:

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

When the first await is called, the debugger never reach the next line and the method never returns. 调用第一个await ,调试器永远不会到达下一行,并且方法永远不会返回。 The string path variable has proper content. string path变量具有适当的内容。

So far I have tried to use ConfigureAwait(false) , but it doesn't work in my case. 到目前为止,我已经尝试使用ConfigureAwait(false) ,但它在我的情况下不起作用。

I have also found out in the topic: Deadlock while using async await , that: 我也在主题中发现: 使用异步等待时的死锁 ,即:

When you're using ConfigureAwait(false) , you tell your program you dont mind about the context. 当您使用ConfigureAwait(false) ,您告诉您的程序您不介意上下文。 It can solve some deadlocking problems, but isn't usually the right solution. 它可以解决一些死锁问题,但通常不是正确的解决方案。 The right solution is most likely never to wait for tasks in a blocking way, and being asynchronous all the way. 正确的解决方案很可能永远不会以阻塞的方式等待任务,并且一直是异步的。

I don't know where I could have that stuff in a blocking way. 我不知道在哪里可以阻挡这些东西。 What can be the reason for that deadlock? 造成这种僵局的原因是什么?

And if it's all about wrong approach, do you know any pattern that would be more appropriate for downloading thumbnails to a collection of items? 如果这是关于错误的方法,你知道任何更适合将缩略图下载到一组项目的模式吗?

Thank you for your help. 谢谢您的帮助。


update: how is GetImage invoked: It's like in topic: WP 8.1 Binding image from a http request 更新:如何调用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 in xaml: 并在xaml中:

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

Try maybe to call you GetImage into Task.Factory.Run to force the task to be executed in another thread. 尝试可能会将GetImage调用到Task.Factory.Run中以强制任务在另一个线程中执行。

Attention also at you Bitmap creation you could have problem with it because the bitmap is not created in the UI thread. 您也可以注意创建位图,因为位图不是在UI线程中创建的。

First, if you are on Windows Phone 8.1 then it is recommended to use the HttpClient from the namespace Windows.Web.Http and not from System.Net.Http . 首先,如果您使用的是Windows Phone 8.1,则建议使用命名空间Windows.Web.HttpHttpClient ,而不是System.Net.Http Some reasons are explained on this link . 此链接上解释了一些原因。

My answer uses the new Windows.Web.Http.HttpClient so your GetImage method can look like this: 我的回答使用新的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;
            }
        }

I have tested this method on a sample URL and it displayed correctly in an Image control. 我已经在示例URL上测试了此方法,并在Image控件中正确显示。

Second, why don't you just let the Image control handle downloading the BitmapImage without the converter, so that your XAML looks like this: 其次,为什么不让Image控件在没有转换器的情况下处理下载BitmapImage ,这样你的XAML看起来像这样:

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

This way your ImagePath can be a path to a resource from the internet and a local resource. 这样,您的ImagePath就可以成为来自Internet和本地资源的资源的路径。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM