下载图像时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"/>
首先,如果你是在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线程中创建的。