异步调用异步,'marshalled for a different thread'消息



我在XAML中有一个Windows 8商店应用程序。我有一个绑定到属性的Image控件,但因为我想要显示的URL需要身份验证,所以我必须创建一个网络响应并以这种方式生成位图,而不仅仅是向我的图像控件提供URL。

问题是,内存流操作是异步的,对象的属性必须是同步的,而不是异步的。所以我有一个非常简单的设置:

public ImageSource ImageSource
{
get { return Task.Run(() => BitmapImageUtils.ToImage(this.Upload.ThumbFile)).Result; }

并且Image控件具有ImageSource属性作为其绑定。问题是,我收到以下异常。ListView中有多个Image控件,它们都以这种方式绑定,我的猜测是,调用这个控件的UI线程以某种方式将控件交给一个线程,然后试图以某种方式返回。我对此有点陌生。

该应用程序调用了一个为不同线程整理的接口。(HRESULT:0x8001010E(RPC_E_WRONG_THREAD)异常)

感谢您的帮助。

---编辑ToImage方法

public static async Task<BitmapImage> ToImage(byte[] byteArray)
{
var bitmapImage = new BitmapImage();
var stream = new InMemoryRandomAccessStream();
await stream.WriteAsync(byteArray.AsBuffer());
stream.Seek(0);
await bitmapImage.SetSourceAsync(stream);
return bitmapImage;
}

异常(注意,异常是聚合异常的内部异常,我认为这是异步/等待异常的标准异常

at Windows.UI.Xaml.Media.Imaging.BitmapImage..ctor()
at Campfire.Utils.BitmapImageUtils.<ToImage>d__0.MoveNext()

核心问题是许多UI类型(包括BitmapImage)具有UI线程亲和性,而您当前的代码正试图在后台线程(在Task.Run中)上创建它们。

我建议你重新设计你的房产;无论如何,您都不应该在I/O操作上阻止它。我的AsyncEx库中有一个类型,它允许您将数据绑定到Task<T>的结果。

您的财产变成:

public INotifyTaskCompletion<ImageSource> ImageSource { get; private set; }

当你想开始下载时,你可以:

ImageSource = NotifyTaskCompletion.Create(BitmapImageUtils.ToImage(Upload.ThumbFile)));

然后,您的数据绑定将从ImageSource更改为ImageSource.Result。还有其他属性(例如,ImageSource.IsFaultedImageSource.ErrorMessage)允许您使用数据绑定处理其他结果。注意,INotifyTaskCompletion<T>.Result而不是阻塞;它将只返回默认值,直到任务完成。

p.S.AggregateExceptionasync/await中不正常。你看到它的唯一原因是因为你使用的是Task<T>.Result;如果await是同一个任务,则会得到一个异常,该异常不是封装在AggregateException中的。

为什么不将LoadImage添加到带有属性的对象中,并让它以异步方式进行加载。一旦加载完所有内容,就可以正常设置ImageSource属性。

最新更新