2012-03-27 52 views
3

这是怎么回事?从委托内锁定静态对象不起作用

private static object Lock_HandleError = new object(); 
public static void HandleError(Exception ex) 
{ 
    lock(Lock_HandleError) 
    { 
     //IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442 
     //The solution is to dispatch and queue the MessageBox. We must use BeginInvoke because dispatcher processing is suspended in such cases. 
     Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate() 
     { 
      lock(Lock_HandleError) 
      { 
       Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate(){ 
        HandleError(new Exception("testing purposes only")); 
       }, DispatcherPriority.Background); 

       MessageBox.Show(ex.Message, "Application Error", MessageBoxButton.OK, MessageBoxImage.Error); 
       //This point is not reached until the user clicks "OK" 
      } 
     }, DispatcherPriority.Background); 
    } 
} 

public void main() 
{ 
    HandleError(new Exception("The first error")); 
} 

上述代码的预期的行为是一个错误信息会出现在一个时间,当用户点击“确定”后,在Lock_HandleError对象将得到派出线程释放,下一调用HandleError可以继续 - 但是我得到的是无限级错误消息,没有打到“OK”。

为什么这个锁不工作?

通过在每个锁定语句的入口和出口处设置断点,我可以清楚地看到代理正在调用lock(),并再次调用一个新的“HandleError”调用,然后在MessageBox上暂停以等待用户输入。

与此同时,在另一个线程中,调度HandleError的调用得到运行 - 而不是像它应该那样在lock()语句中等待,即使MessageBox委托清楚地放置了一个锁并且还没有发布它。

+0

我对我的多线程不太熟悉,但是我想知道是否可以尝试在'BeginInvoke'方法中删除'lock'? – Richard 2012-03-27 16:07:54

+0

删除锁定并不能完成任何操作 - 因为锁定就像现在不存在一样。虽然UI线程正在等待用户在消息框中单击“确定”,但它会处理来自应用程序其他区域的HandleError()的额外调用,并且我需要它停止并等待。 – Alain 2012-03-27 16:34:50

+0

我试图在这里使用信号量来解决问题:http://stackoverflow.com/questions/9894750/how-can-i-get-the-ui-thread-to-wait-on-a-semaphore-but -process-additional-dispa – Alain 2012-03-27 17:48:54

回答

6

两个部分答案:

  1. 明白,锁重入的。当一个线程已经在一个对象上持有一个锁时,该线程可以一次又一次地使用同一个锁而不会阻塞。

  2. 第一个MessageBox启动时,UI线程仍在泵送消息,因此在UI线程上正在处理对HandleError的后续(递归)调用(因为它已经保存了锁,可以重新输入它)。

+0

到目前为止,这是最完整的解释,因为我都希望MessageBox能够阻止它运行的线程(根据'2.'它没有),我期待锁可以像信号量一样阻塞,不管哪个线程拥有它(根据'1.'它不)。虽然我的问题没有明确要求解决我的问题的方法,但为什么我的方法不起作用,我仍然有兴趣知道是否有解决这类问题的简单方法。 – Alain 2012-03-27 16:38:26

6

为什么这个锁不工作?

线程被允许输入它已经拥有的锁定语句。在本质上,锁不会阻塞自己的线程。

因此,发生了什么事是原始线程需要锁,然后才允许将消息添加到分派器的队列中。它可以添加尽可能多的想要的。

Dispatcher在处理时获取第一条消息,然后调用HandleError。由于这是在调度程序线程中运行的,因此它可以进入外部和内部锁定,并再次调用HandleError,以无限循环的方式递增地向队列中添加新消息。

+0

感谢您的解释。显然,lock()不是我想要使用的。我怎样才能达到我的目标? – Alain 2012-03-27 16:10:25

+0

@Alain你为什么用BeginInvoke递归调用自己?这将是有问题的,因为任何给予你以后的行为的东西都会导致你死锁。 – 2012-03-27 16:17:42

+0

这是模拟实际发生的事情,这是许多不同的线程遇到同时发生的错误,并且它们都会在UI线程上调用'HandleError()',而不是等待一个MessageBox在处理下一个错误之前被解散,应用程序会同时级联所有错误。如果我们有一个反复发生的错误,这个锁不起作用,使我无法检测和处理无限级别的消息框。 – Alain 2012-03-27 16:23:02