0

我知道我以前看过一篇关于此的文章或 SO,但现在似乎找不到。我在帮助同事编写测试时遇到了一个问题,该测试正在检查跨多个线程发生的 UI 操作(我意识到这里的问题......这不是我现在想要关注的 :))。代码看起来类似于这种伪代码:

[RequiresSTA]
Test
{
  var tb = new Textbox();
  tb.DoSomethingAsyncAndThenUpdateTB() //This is done via tb.SetValue being called
}

...

DoSomethingAsyncAndThenUpdateTB()
{
  var bw = new BackgroundWorker();
  bw.DoWork += ...Do Stuff...
  bw.RunWorkerCompleted += { tb.Text = "foo";}
}

我遇到的问题OnComplete是引发跨线程异常。但是,一切都应该在 STA 线程上创建。我相信问题在于 UI 元素不是在创建时附加到它们的线程,而是在稍后的时间点......我的文本框最终附加到不是 STA 的线程?或者也许是后台工作人员?

问题:

UI 元素何时真正附加到线程?

4

2 回答 2

2

所以首先,不只是一个 STA 线程。任何线程在创建时都可以创建为STA线程。UI 线程需要是 STA 线程,但并非所有 STA 线程都是 UI 线程。

接下来,您BackgroundWorker需要通过某种方式了解 UI 线程是什么。它能够将某些事件编组到 UI 线程并不神奇。它所做的是查看SynchronizationContext.Current其构造函数中的值。然后它捕获该上下文的值,并稍后将其用作将所有非工作事件发布到的上下文定义。如果您在 UI 线程之外创建 BGW,那么它稍后将无法编组回 UI 线程。

如果您有一个用作 UI 线程的 STA 线程,但您没有设置 的值SynchronizationContext.Current,那么 BGW 将无法执行它的操作。

至于问题:

UI 元素何时真正附加到线程?

在它的构造函数中。

于 2013-08-08T20:13:45.013 回答
0

每个 ui 元素实现 DependencyObject,后者实现抽象类 DispatcherObject.. 创建 DispatcherObject 时,它会将当前调度程序分配给 DispatcherObject.. 稍后,每当您访问 UI 元素的任何属性时,都会有一个验证来检查您是否正在访问它来自 UI 线程。

这是在 VerifyAccess 中完成的

/// <summary>Enforces that the calling thread has access to this <see cref="T:System.Windows.Threading.DispatcherObject" />.</summary>
    /// <exception cref="T:System.InvalidOperationException">the calling thread does not have access to this <see cref="T:System.Windows.Threading.DispatcherObject" />.</exception>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void VerifyAccess()
    {
        Dispatcher dispatcher = this._dispatcher;
        if (dispatcher != null)
        {
            dispatcher.VerifyAccess();
        }
    }

如果调用线程不是与调度程序关联的线程,则抛出异常

public void VerifyAccess()

{ if (!this.CheckAccess()) { throw new InvalidOperationException(SR.Get("VerifyAccess")); } }

这称为线程关联

于 2013-08-08T20:24:24.677 回答