试图从后台线程更新UI:为什么它有时会抛出,有时什么都不做?

时间:2011-05-12 17:15:52

标签: .net wpf multithreading

我在WPF中注意到,当尝试从后台线程更新UI时(我知道你不应该这样做 - 只是玩弄东西),有时它会抛出InvalidOperationException,有时它只是什么也没做。当我不正确地尝试从异步WCF调用启动的后台线程更新UI时,我首先注意到这一点(使用Begin / End,而不是自动编组到UI线程的事件模型)。

例如,假设我有一个带有按钮和复选框的简单表单。此代码将抛出一个InvalidOperationException(“调用线程无法访问此对象,因为不同的线程拥有它。”)每次:

private void button1_Click(object sender, RoutedEventArgs e)
{
    new Thread(() => checkBox1.IsChecked = true).Start();
}

现在,采用相同的形式,并在某处向一个简单的WCF服务添加一个标准的服务引用。然后试试这个:

private void button1_Click(object sender, RoutedEventArgs e)
{
    var client = new MyServiceClient();
    //Note the use of Begin/End as opposed to the eventing model - Callback
    //will not be called on the UI thread, but a worker thread. I have
    //verified this through the debugger thread list and by checking the 
    //result of Dispatcher.CheckAccess() in the callback.
    client.BeginMyServiceMethod("MyArgument", Callback, null);
}

private void Callback(IAsyncResult result)
{
    //If I call Dispatcher.CheckAccess() here, it returns false,
    //but if I call Dispatcher.VerifyAccess() it does not throw!
    checkBox1.IsChecked = true;  // no exception, no effect
}

我的理解是在该回调方法中调用Dispatcher.VerifyAccess()应该抛出,就像试图操纵checkBox1上的任何东西一样。相反,没有任何反应 - UI中的复选框不会被检查,也不会抛出任何异常。有谁知道为什么会这样?

2 个答案:

答案 0 :(得分:1)

调用回调方法的Framework代码是否可能吞噬异常?在回调中放置try/catch以查看是否抛出异常。如果是这样,那么您就知道框架正在吞噬异常。

答案 1 :(得分:0)

通常,大多数GUI环境都不支持(以及那些甚至不鼓励的人)从操作不同线程的小部件。通常,只有一个线程处理给定的GUI。大多数GUI工具包提供了一种机制,您可以通过该机制调用gui线程上的“任务”,从而安全地操作小部件。对于你希望做的小部件的每个tweek来创建一个新的任务,这是一个单调乏味的练习,但实际上没有其他选择。

对于C#和wpf(已经有一段时间了)我相信机制围绕着:

widget.Dispatcher.BeginInvoke(delegate, args[])

因此,对于您希望执行的小部件的每个tweek,您创建一个委托来执行该tweek,然后通过小部件Dispatcher(处理在小部件gui线程上调度事件的对象)调用它。

参考

BeginInvoke