2012-03-27 1 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")); 
} 

Ожидаемое поведение выше кода является то, что один появится сообщение об ошибке, в то время, и когда пользователь нажимает кнопку «OK», то на Lock_HandleError объект освобождается от блокировки от посланной нити, а следующий вызов HandleError может продолжаться - но то, что я получаю, - это бесконечный каскад сообщений об ошибках, даже не нажимая «ОК».

Почему этот замок не работает?

Установив точки останова при вводе и выходе из каждого оператора блокировки, я ясно вижу, что делегат вызывает lock() и снова отправляет новый вызов в «HandleError», а затем приостанавливает работу MessageBox, ожидая ввода пользователя.

Между тем, в другом потоке этот вызов HandleError запускается, но вместо ожидания в операторе lock(), как и следовало ожидать, он просто взрывается через него, хотя делегат MessageBox явно размещает блокировку и еще не успел выпустил его.

+0

Я не могу поцарапать свою многопоточность, но мне интересно, можно ли попытаться удалить «блокировку» в методе «BeginInvoke»? – Richard

+0

Удаление замка ничего не может сделать, потому что замок действует так, как будто он даже не существует. Пока поток пользовательского интерфейса ожидает, чтобы пользователь нажал «ОК» в окне сообщения, он обрабатывает дополнительные вызовы HandleError(), поступающие из других областей приложения, и мне нужно его остановить и подождать. – Alain

+0

Я попытался решить проблему с помощью семафоров здесь: http://stackoverflow.com/questions/9894750/how-can-i-get-the-ui-thread-to-wait-on-a-semaphore-but -process-extra-dispa – Alain

ответ

6

Две части Ответ:

  1. Поймите, что замки Реентрантная. Когда нить уже удерживает блокировку объекта, этот поток может принимать один и тот же замок снова и снова без блокировки.

  2. Пока первый MessageBox встает, поток пользовательского интерфейса по-прежнему накачивает сообщения, поэтому последующие (рекурсивные) вызовы HandleError обрабатываются в потоке пользовательского интерфейса (который, поскольку он уже содержит блокировку, может повторно ввести его).

+0

Пока это самое полное объяснение, потому что я ожидал, что MessageBox заблокирует поток, на котором он работал (в соответствии с '2.' это не так), и я ожидал блокирует блокировку как семафоры, независимо от того, какой поток принадлежит ему (согласно '1.' это не так). Хотя в моем вопросе нет явного запроса о решении моей проблемы, которая действительно работает, только, почему моя работа не работает, мне все равно интересно узнать, есть ли легкое решение для такого рода проблем. – Alain

6

Почему этот замок не работает?

Поток разрешен для ввода оператора блокировки, которому он уже владеет. В сущности блокировка не блокирует собственный поток.

Таким образом, происходит то, что происходит в исходном потоке, и блокируется, а затем разрешено добавлять сообщения в очередь диспетчера. Он может добавить столько, сколько захочет.

Диспетчер при обработке получает первое сообщение, а затем вызывает HandleError. Поскольку это работает в потоке диспетчера, ему разрешено вводить внешний и внутренний блокировки и снова вызвать HandleError, рекурсивное добавление новых сообщений в очередь в бесконечном цикле.

+0

Спасибо за объяснение. Ясно, что lock() не то, что я хочу использовать. Как я могу достичь того, что я намеревался? – Alain

+0

@Alain Почему вы называете себя рекурсивно с BeginInvoke? Это будет проблематично, так как все, что дало вам поведение, которое вы были после, эффективно блокировало бы вас. –

+0

Это то, что происходит на практике, что много разных потоков испытывает одновременные ошибки, и все они отправляют вызов 'HandleError()' в потоке пользовательского интерфейса, - но вместо того, чтобы ждать, пока один MessageBox не будет уволен, перед тем, как обработать следующую ошибку , приложение каскадирует все ошибки одновременно. Если у нас есть повторяющаяся ошибка, эта блокировка не работает, это мешает мне обнаруживать и обрабатывать бесконечный каскад ящиков сообщений. – Alain

Смежные вопросы