我有一个有点复杂的WPF应用程序,当尝试使用调度程序在UI线程上调用调用时,似乎是“挂起”或卡在Wait调用中。
一般过程是:
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly.
我的问题是,当调度调度程序调用时,它似乎每次都挂起,并且callstack指示它处于休眠状态,等待或加入Invoke实现。
那么,有什么我做错了,我错过了,明显与否,或者是否有更好的方法调用UI线程来设置这个属性(以及其他)?
编辑:解决方案是在线程委托的末尾调用System.Windows.Threading.Dispatcher.Run()(例如,正在执行工作的地方) - 感谢所有帮助过的人
答案 0 :(得分:10)
调用是同步的 - 您需要Dispatcher.BeginInvoke。另外,我相信你的代码示例应该在“else”语句中移动“SetValue”。
答案 1 :(得分:8)
我认为这可以通过代码更好地显示出来。请考虑以下情况:
线程A执行此操作:
lock (someObject)
{
// Do one thing.
someDispatcher.Invoke(() =>
{
// Do something else.
}
}
线程B执行此操作:
someDispatcher.Invoke(() =>
{
lock (someObject)
{
// Do something.
}
}
乍一看,一切看起来都很精致,但事实并非如此。这将产生僵局。调度程序就像一个线程的队列,当处理像这样的死锁时,以这种方式思考它们很重要:“以前的调度可能会阻塞我的队列?”。线程A将进入...并在锁定下发送。但是,如果线程B在标记为“做一件事”的代码中的线程A进入的时候会怎样?嗯......
你现在有一个漂亮的僵局。
答案 2 :(得分:5)
你说你正在创建一个新的STA线程,这个新线程的调度程序是否正在运行?
我从“this.Dispatcher.Thread!= Thread.CurrentThread”得到你希望它是一个不同的调度员。确保它正在运行,否则它不会处理它的队列。
答案 3 :(得分:3)
我认为你的意思是(!this.Dispatcher.CheckAccess())
我也在使用Invoke进行挂起,或者如果我可以在BeginInvoke中调用我的委托 - 似乎在做这一切: - (
答案 4 :(得分:2)
这听起来像一个僵局;如果调用.Invoke的线程已经拥有一个锁定/互斥锁/ etc,UI线程需要完成它的工作,这通常会发生。最简单的方法是使用BeginInvoke:这样,当前线程可以继续运行,并且(可能)很快就会释放锁 - 允许UI获取它。或者,如果您可以识别有问题的锁定,则可以在一段时间内故意释放它。
答案 5 :(得分:1)
我遇到了类似的问题,虽然我仍然不确定答案是什么,但我认为你的
if(this.Dispatcher.Thread != Thread.CurrentThread)
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
应替换为
if(this.Dispatcher.CheckAccess())
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{
this.Name = value; // Call same setter, but on the UI thread
});
return;
}
CheckAccess不会出现在Intellisense中,但它就在那里并且就是为了这个目的。此外,我同意一般你想在这里使用BeginInvoke,但是我发现当我执行此异步时我没有获得UI更新。不幸的是,当我同步进行时,我遇到了死锁状态...
答案 6 :(得分:0)
我知道这是一个旧线程,但这是另一种解决方案。
我刚刚解决了类似的问题。我的调度员运行正常,所以......
我必须展示DEBUG - > THREAD WINDOW标识在任何地方执行我的代码的所有线程。
通过检查每个线程,我很快看到哪个线程导致了死锁。
这是多个线程组合lock (locker) { ... }
语句,并调用Dispatcher.Invoke()。
在我的情况下,我可以更改特定的lock (locker) { ... }
语句,并将其替换为Interlocked.Increment(ref lockCounter)
。
这解决了我的问题,因为避免了死锁。
void SynchronizedMethodExample() {
/* synchronize access to this method */
if (Interlocked.Increment(ref _lockCounter) != 1) { return; }
try {
...
}
finally {
_mandatoryCounter--;
}
}