我知道我以前看过一篇文章或者上面的内容,但现在似乎无法找到它。我遇到了一个问题,帮助一个同事编写了一个测试,该测试检查了多个线程中发生的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元素何时实际附加到线程?
答案 0 :(得分:2)
首先,不只有一个STA线程。任何线程都可以在创建时创建为 STA线程。 UI线程需要是STA线程,但并非所有STA线程都是UI线程。
接下来,您遇到BackgroundWorker
需要知道UI线程是什么的问题。它能够将某些事件封送到UI线程并不神奇。它的作用是在其构造函数中查看SynchronizationContext.Current
的值。然后它捕获该上下文的值,并在稍后使用它作为上下文的定义以将所有非工作事件发布到。如果你在UI线程之外创建BGW,那么它以后就无法编组回UI线程。
如果你有一个STA线程用作你的UI线程,但你没有设置SynchronizationContext.Current
的值,那么BGW将无法做到这一点。
至于问题:
UI元素何时实际附加到线程?
在其构造函数中。
答案 1 :(得分: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()) { 抛出新的InvalidOperationException(SR.Get(“VerifyAccess”)); } }
这称为线程亲和力