Normalerweise, wenn der UI-Thread etwas wie MessageBox.Show()
ruft die aktuelle Codeausführung wird nicht fortgesetzt, bis der Benutzer auf OK klickt, aber das Programm wird auch weiterhin anderen Code auszuführen geschickt auf dem UI-Thread .Wie kann ich den UI-Thread auf einen Semaphor warten lassen, aber zusätzliche Dispatcher-Anfragen verarbeiten? (Wie das, was MessageBox.Show tut nativ)
In this question, hatte ich ein Problem mit zu vielen Delegierten auf dem UI-Thread geschickt wird sofort aufgerufen. Ich wollte an bestimmten Punkten pausieren, bevor ich die Ausführung fortsetzte.
In meiner neuen Fehlerbehandlung, verwende ich Semaphore nicht um sicherzustellen, dass mehr als ein Fehler auf einmal behandelt wird. Ich versende eine MessageBox, um den Benutzer zu benachrichtigen, und wenn sie auf "OK" klicken, lasse ich den Semaphor los, damit der nächste Fehler verarbeitet werden kann.
Das Problem ist, dass es wie erwartet nicht benimmt. Wenn zwei abgefragte Aufrufe an HandleError gleichzeitig erfolgen, leitet die erste einen Aufruf an MessageBox.Show ab, und die zweite blockiert den UI-Thread. Merkwürdigerweise wird der geschickte Aufruf MessageBox.Show()
nie ausgeführt - die gesamte Anwendung hängt nur - so die Semaphore, der freigegeben werden soll ist, wenn der Benutzer auf „OK“ geklickt wird dauerhaft gesperrt. Was fehlt dieser Lösung?
private static ConcurrentDictionary<Exception, DateTime> QueuedErrors = new ConcurrentDictionary<Exception, DateTime>();
private static Semaphore Lock_HandleError = new Semaphore(1, 1); //Only one Error can be processed at a time
private static void ErrorHandled(Exception ex)
{
DateTime value;
QueuedErrors.TryRemove(ex, out value);
Lock_HandleError.Release();
}
private static bool ExceptionHandlingTerminated = false;
public static void HandleError(Exception ex, string extraInfo = "", bool showMsgBox = true, bool resetApplication = true)
{
if(ExceptionHandlingTerminated || App.Current == null) return;
QueuedErrors.TryAdd(ex, DateTime.Now); //Thread safe tracking of how many simultaneous errors are being thrown
Lock_HandleError.WaitOne(); //This will ensure only one error is processed at a time.
if(ExceptionHandlingTerminated || App.Current == null)
{
ErrorHandled(ex);
return;
}
try
{
if(QueuedErrors.Count > 10)
{
ExceptionHandlingTerminated = true;
throw new Exception("Too many simultaneous errors have been thrown in the background.");
}
if(Thread.CurrentThread != Dispatcher.CurrentDispatcher.Thread)
{
//We're not on the UI thread, we must dispatch this call.
((App)App.Current).Dispatcher.BeginInvoke((Action<Exception, string, bool, bool>)
delegate(Exception _ex, string _extraInfo, bool _showMsgBox, bool _resetApplication)
{
ErrorHandled(_ex); //Release the semaphore taken by the spawning HandleError call
HandleError(_ex, _extraInfo, _showMsgBox, _resetApplication);
}, DispatcherPriority.Background, new object[] { ex, extraInfo, showMsgBox, resetApplication });
return;
}
if(showMsgBox)
{
//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<Exception, String>)delegate(Exception _ex, String _ErrMessage)
{
MessageBox.Show(_ErrMessage, "MUS Application Error", MessageBoxButton.OK, MessageBoxImage.Error);
ErrorHandled(_ex); //Release the semaphore taken by the spawning HandleError call
}, DispatcherPriority.Background, new object[]{ ex, extraInfo });
}
else
{
ErrorHandled(ex);
}
}
catch(Exception terminatingError)
{
ExceptionHandlingTerminated = true;
Dispatcher.CurrentDispatcher.BeginInvoke((Action<String>)delegate(String _fatalMessage)
{
MessageBox.Show(_fatalMessage, "Fatal Error", MessageBoxButton.OK, MessageBoxImage.Stop);
if(App.Current != null) App.Current.Shutdown(1);
}, DispatcherPriority.Background, new object[] { fatalMessage });
ErrorHandled(ex); //Release the semaphore taken by this HandleError call which will allow all other queued HandleError calls to continue and check the ExceptionHandlingTerminated flag.
}
}
Mach dir keine Sorgen über die fehlende Zeichenkette, ich habe viele Details ausgeschnitten, um das Muster klarer zu machen.
Haben Sie es mit anderen Werten von 'DispatcherPriority' versucht? –