C# 使用UI线程调度程序调用为不同的线程异常封送接口?
我正在开发一个用MVVM Light编写的WinRT/Windows应用商店应用程序。我正在尝试修改MVVM Light的OnPropertyChanged()方法,以便事件通知始终发生在UI线程上,否则绑定到视图模型属性的UI元素将错过事件通知,因为它们必须发生在UI线程上。目前我正在尝试以下代码:C# 使用UI线程调度程序调用为不同的线程异常封送接口?,c#,windows-store-apps,async-await,mvvm-light,deadlock,C#,Windows Store Apps,Async Await,Mvvm Light,Deadlock,我正在开发一个用MVVM Light编写的WinRT/Windows应用商店应用程序。我正在尝试修改MVVM Light的OnPropertyChanged()方法,以便事件通知始终发生在UI线程上,否则绑定到视图模型属性的UI元素将错过事件通知,因为它们必须发生在UI线程上。目前我正在尝试以下代码: /// <summary> /// Event handler for the PropertyChanged event. /// </summary> /// <
/// <summary>
/// Event handler for the PropertyChanged event.
/// </summary>
/// <param name="propertyName">The name of the property that has changed.</param>
protected void OnPropertyChanged(string propertyName = null)
{
var eventHandler = PropertyChanged;
if (eventHandler != null)
{
// Make sure the event notification occurs on the UI thread or the INotifyPropertyChanged
// notification will not be seen by the consumer of this event or worse,
// a "wrong thread" COM Exception will be raised if we are not on the UI thread.
var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
dispatcher.RunAsync(dispatcher.CurrentPriority,
() =>
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
});
}
}
//
///PropertyChanged事件的事件处理程序。
///
///已更改的属性的名称。
受保护的OnPropertyChanged无效(字符串propertyName=null)
{
var eventHandler=PropertyChanged;
if(eventHandler!=null)
{
//确保事件通知发生在UI线程或INotifyPropertyChanged上
//消费者不会看到此事件或更糟事件的通知,
//如果我们不在UI线程上,将引发“错误线程”COM异常。
var dispatcher=Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.dispatcher;
dispatcher.RunAsync(dispatcher.CurrentPriority,
() =>
{
eventHandler(这是新的PropertyChangedEventArgs(propertyName));
});
}
}
我以前使用过这样的代码将代码推送到UI线程上。但是,在这种情况下,我得到以下例外情况:
中发生了类型为“System.exception”的首次意外异常
公共_WinStore.DLL
WinRT信息:应用程序调用的接口
为不同的线程编组
这是令人困惑的,因为CoreWindow.Dispatcher的目的是将代码的执行从非UI线程调度到UI线程。我在SO上阅读了许多封送处理错误线程,但在尝试调用调度程序本身的RunAsync()时,没有一个线程处理封送处理异常。相反,它们涵盖了使用我正在使用的代码来解决异常的发生
为什么我在Dispatcher.RunAsync()中遇到此异常,以及如何修复它
上下文注释:我陷入这种情况是因为出现了死锁,一些代码是由绑定到视图模型中WriteableBitmap属性的UI元素触发的。当等待的异步任务试图在原始线程(即UI线程)上继续,并且该线程正在等待调用完成时,等待调用将死锁。为了解决这个问题,我在await语句中添加了ConfigureAwait(false),以从UI线程上下文中释放调用。当MVVM Light的OnNotifyPropertyChanged()尝试更新属性时,这导致错误的线程COM异常。这就是为什么我试图将代码推回到UI线程。无论如何,我都想这样做,因为如果我成功了,我就不必担心事件通知会正常发生,不管调用上下文如何
更新:我按照Stephen Cleary的建议添加了AsyncEx库。到目前为止,它运行得非常好。一开始我遇到了一个难题,正如你在我对他的回答的回复中所看到的。我拥有的对象包含一个由SQLite引擎反序列化为的JPEG字节字段。这意味着我的构造函数是无参数的,因此启动异步方法将JPEG字节转换为可绑定的WriteableBitmap对象失败。发生这种情况是因为异步转换方法在转换所需的字节被反序列化之前开始运行
我用一个起初处于未设置状态的AsyncManualResetEvent对象解决了这个问题。JPEG字节属性的属性设置程序设置事件。异步转换方法正在等待此事件对象,因此在字节可用后立即释放该方法进行转换。我在下面发布代码摘录,看看Stephen和你们中的其他人是否看到了实现中的任何潜在问题,或者是否有更简单的方法来实现这一点:
// Create an Async manual reset event for the JPEG conversion method in the unset state.
private AsyncManualResetEvent _asyncManResetEvent = new AsyncManualResetEvent(false);
/// <summary>
/// The jpeg bytes for the JPEG image for the videomark.
/// </summary>
private byte[] _thumbnailJpegBytes;
public byte[] ThumbnailJpegBytes
{
get
{
return this._thumbnailJpegBytes;
}
set
{
SetProperty(ref this._thumbnailJpegBytes, value, "ThumbnailJpegBytes");
// Release the lock so that the async call waiting to convert the JPEG bytes to
// a WriteableBitmap can finish up.
_asyncManResetEvent.Set();
}
}
// The WriteableBitmap property.
[Ignore]
public INotifyTaskCompletion<WriteableBitmap> ThumbnailAsync
{
get;
private set;
}
// The async method passed to the NotifyTaskCompletion constructor.
async private Task<WriteableBitmap> ConvertJpegBytesToThumbnail()
{
// Wait for data to be available.
await this._asyncManResetEvent.WaitAsync();
return await Misc.JpegBytesToBitmap(ThumbnailJpegBytes);
}
//为处于未设置状态的JPEG转换方法创建异步手动重置事件。
专用AsyncManualResetEvent _asyncManResetEvent=新的AsyncManualResetEvent(错误);
///
///videomark的jpeg图像的jpeg字节。
///
专用字节[]_个thumbnailJpegBytes;
公共字节[]ThumbnailJpegBytes
{
得到
{
返回此。\u thumbnailJpegBytes;
}
设置
{
SetProperty(请参考此项。_thumbnailJpegBytes,值,“thumbnailJpegBytes”);
//释放锁,以便异步调用等待将JPEG字节转换为
//一个可写的位图可以结束。
_asyncManResetEvent.Set();
}
}
//WriteableBitmap属性。
[忽略]
public INotifyTaskCompletion ThumbnailAsync
{
得到;
私人设置;
}
//传递给NotifyTaskCompletion构造函数的异步方法。
异步专用任务转换程序JPEGByTestOthumbNail()
{
//等待数据可用。
等待此消息。_asynchmanresetevent.WaitAsync();
返回wait Misc.JpegBytesToBitmap(ThumbnailJpegBytes);
}
在我看来,这是解决问题的错误方法。UI组件(显然)具有UI亲和力。我认为任何绑定到这些UI组件的数据都具有UI亲和力
所以,核心问题是您正在阻塞数据绑定属性。无论如何都应该避免这种情况,尤其是在Windows应用商店应用程序中。我有一篇讨论如何使用的博客文章
总之,您应该使用类似my AsyncEx库的类型来异步初始化属性值。此解决方案提供了两个好处:您的应用程序响应迅速(不阻塞),并且在引发
PropertyChanged
事件之前,更新属性的代码被适当地封送到UI线程。我重现了此问题并找到了实际原因。。。它在dispatcher.CurrentPriority中
这是一个属性结果