Yes, there's a race here. A takes a good millisecond before the target starts running. It will work 'better' if you use Control.BeginInvoke() instead, the form's Dispose() implementation will empty the dispatch queue.
But that's still a race, albeit that it will strike very rarely. Your code as written in the snippet doesn't require Invoke().
Yes, there's a race here. A takes a good millisecond before the target starts running. It will work 'better' if you use Control.BeginInvoke() instead, the form's Dispose() implementation will empty the dispatch queue.
But that's still a race, albeit that it will strike very rarely. Your code as written in the snippet doesn't require Invoke(). The only clean fix is to interlock the FormClosing event and to delay the close until you got confirmation that the background thread is completed and can't be started again.
Not easy to do with your code as is since that requires a 'completed' callback so you can really get the form closed. BackgroundWorker would be a better mousetrap. The Q&D fix is to catch the ObjectDisposedException that BeginInvoke will raise.
Given how rare this will be when you use BeginInvoke(), that ugly hack could be palpable. You just can't test it :).
That may at least mitigate it. My code as written doesn't require Invoke? These exceptions are thrown about 1 out of 10 times.
I can test them! :) – drifter Aug 25 '10 at 13:10 It doesn't, no need to delay the thread and wait for the delegate to finish running. You're not doing anything after the Invoke call.
You can't really test it once you use BeginInvoke, you'll need to open and close your form at least a million times. Rest assured that the race is still there, you'll need to catch ODE. – Hans Passant Aug 25 '10 at 13:17 I wouldn't mind catching the ObjectDisposedException, but sometimes it throws InvalidOperationException instead ("Invoke or BeginInvoke cannot be called on a control until the window handle has been created.") I can't catch that unless I can differentiate it from other InvalidOperationException's in the invoked code, right?
– drifter Aug 25 '10 at 13:21 Are you sure you don't call MyMethod too early? It won't be safe to call until the Load event has run. – Hans Passant Aug 25 '10 at 13:34 Positive.
The exception only occurs when the form closes. – drifter Aug 25 '10 at 13:38.
Take a look at WindowsFormsSynchronizationContext. The Post method posts call to your UpdateUI delegate on the UI thread without needing a dedicated window; this lets you skip calling IsHandleCreated and Invoke. Edit: MSDN has some code examples under "Multithreaded Programming with the Event-based Asynchronous Pattern".
You might find it easier to program via the AsyncOperationManager class, which sits on top of WindowsFormsSynchronizationContext. In turn, the BackgroundWorker component is built on top of AsyncOperationManager. The UI thread is defined as the one on which you call AsyncOperationManager.
CreateOperation; you want to call CreateOperation at the start of MyMethod, when you know you're on the UI thread, and capture its return value in a local variable.
I like that a lot. Some of my users are stubborn to update to . NET 4, but a case is mounting in its favor.
– drifter Aug 25 '10 at 12:55 That's a . NET 2.0 class. Doesn't solve the problem, Control.
Invoke already uses it. Post() will throw ObjectDisposedException. – Hans Passant Aug 25 '10 at 13:06 But WindowsFormsSynchronizationContext posts via a hidden control that's closed after your form is closed, and just before the program exits.
Your form isn't involved at all. – Tim Robinson Aug 25 '10 at 13:18 I am trying to test this, but the documentation seems limited. I'm not sure how to specify the UI thread as the target of the Post method.
– drifter Aug 25 '10 at 13:50 I tried it and what I am seeing is that Post and Send both deadlock when the form is closed between the check of IsHandleCreated and Post/Send. – Brian Gideon Aug 25 '10 at 14:10.
You can check IsDisposed on the form (or any control) before Invoking on it. You should also check this inside of the actual method you're Invoking, in case the form was disposed in the meantime.
The problem is, in some places Invoke is throwing ObjectDisposedException even though it checks IsDisposed immediately before the call. – drifter Aug 25 '10 at 12:59.
I solved this synchronization issue for BeginInvoke by using Hans Passant's recommendation to catch the ObjectDisposedException. So far, it appears to work. I created extension methods of the Control class to facilitate this.
TryBeginInvoke attempts to invoke its own method on the control. If the method is successfully invoked, it checks whether the control has been disposed. If it has been disposed, it returns immediately; otherwise, it calls the method originally passed as a parameter to TryBeginInvoke.
The code is as follows: public static class ControlExtension { // --- Static Fields --- static bool _fieldsInitialized = false; static InvokeDelegateDelegate _methodInvokeDelegate; // Initialized lazily to reduce application startup overhead see method: InitStaticFields static InvokeMethodDelegate _methodInvokeMethod; // Initialized lazily to reduce application startup overhead see method: InitStaticFields // --- Public Static Methods --- public static bool TryBeginInvoke(this Control control, Delegate method, params object args) { IAsyncResult asyncResult; return TryBeginInvoke(control, method, out asyncResult, args); } /// May return true even if the target of the invocation cannot execute due to being disposed during invocation. Public static bool TryBeginInvoke(this Control control, Delegate method, out IAsyncResult asyncResult, params object args) { if (!_fieldsInitialized) InitStaticFields(); asyncResult = null; if (!control. IsHandleCreated || control.
IsDisposed) return false; try { control. BeginInvoke(_methodInvokeDelegate, control, method, args); } catch (ObjectDisposedException) { return false; } catch (InvalidOperationException) // Handle not created { return false; } return true; } public static bool TryBeginInvoke(this Control control, MethodInvoker method) { IAsyncResult asyncResult; return TryBeginInvoke(control, method, out asyncResult); } /// May return true even if the target of the invocation cannot execute due to being disposed during invocation. Public static bool TryBeginInvoke(this Control control, MethodInvoker method, out IAsyncResult asyncResult) { if (!_fieldsInitialized) InitStaticFields(); asyncResult = null; if (!control.
IsHandleCreated || control. IsDisposed) return false; try { control. BeginInvoke(_methodInvokeMethod, control, method); } catch (ObjectDisposedException) { return false; } catch (InvalidOperationException) // Handle not created { return false; } return true; } // --- Private Static Methods --- private static void InitStaticFields() { _methodInvokeDelegate = new InvokeDelegateDelegate(InvokeDelegate); _methodInvokeMethod = new InvokeMethodDelegate(InvokeMethod); } private static object InvokeDelegate(Control control, Delegate method, object args) { if (!control.
IsHandleCreated || control. IsDisposed) return null; return method. DynamicInvoke(args); } private static void InvokeMethod(Control control, MethodInvoker method) { if (!control.
IsHandleCreated || control. IsDisposed) return; method(); } // --- Private Nested Types --- delegate object InvokeDelegateDelegate(Control control, Delegate method, object args); delegate void InvokeMethodDelegate(Control control, MethodInvoker method); }.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.