back on my old unmanaged c++ days, I could trust in my critical sections in a multithreading application. So, now with dotNet/C#, I was relaying on the lock mechanism. By locking a resource I was confident any thread couldn't access those resources within my piece of code.
This seems not to be true in dotNet!
I have my windows service application. I create a main managed thread with an hidden Form hosting a third parity OCX. Within this thread I do message pumping an polling on a list of objects. This list of objects gets modified by events fired by the OCX within this managed thread.
I post simplified parts of my code here:
public bool Start()
{
ServiceIsRunning = true;
m_TaskThread = new Thread(new ParameterizedThreadStart(TaskLoop));
m_TaskThread.SetApartmentState(ApartmentState.STA);
m_TaskThread.Start(this);
return true;
}
private void OnOCXEvent(object objToAdd)
{
lock(m_ObjectList)
{
m_ObjectList.Add(objToAdd); }
}
}
private void CheckList()
{
lock(m_ObjectList)
{
foreach(object obj in m_ObjectList)
{
...
}
}
}
[STAThread] // OCX requirement!
private void TaskLoop(object startParam)
{
try {
...
while (ServiceIsRunning)
{
// Message pump
Application.DoEvents();
if (checkTimeout.IsElapsed(true))
{
CheckList();
}
// Relax process CPU time!
Thread.Sleep(10);
}
} catch(Exception ex) {
...
}
}
You won't beleve me: I got a 'list has been modified' exception in CheckList! 8-/
So I did some logging and I noticed that the OnOCXEvent will be raised when the SAME managed thread is within the CheckList foreach loop. I'm sure: I got the same managed thread id in my log file, the foreach loop wasn't finished and the OnOCXEvent has been called by the same manged thread!
Now I'm wondering: how can this happen? Is a single managed thread implemented with more win32 threads?
Hope someone can explain why this is happening, so I can solve this issue.
Thanks,
Fabio
My Note:
I actually solved the issue creating a copy of the list before the foreach loop. But I do not like this solution. I also like to understand what is happening. I do not own the third parity OCX code, but the method I call within the CheckList loop has logically nothing to do with the OCX event beening fired.
I strongly suspect this is just a re-entrancy issue.
Within your CheckList call you're calling an OCX method. If that does anything which can itself raise OCX events - including effectively calling Application.DoEvents - then you can end up with OnOCXEvent being called in a thread which is also executing CheckList... and that will cause the problem.
This isn't an issue with lock - it's an issue with re-entrancy.
One way to diagnose this would be to modify your CheckList and OnOCXEvent methods:
private bool inCheckList;
private void OnOCXEvent(object objToAdd)
{
lock(m_ObjectList)
{
if (inCheckList)
{
throw new Exception("Look at this stack trace!");
}
m_ObjectList.Add(objToAdd);
}
}
private void CheckList()
{
lock(m_ObjectList)
{
inCheckList = true;
foreach(object obj in m_ObjectList)
{
...
}
inCheckList = false; // Put this in a finally block if you really want
}
}
I strongly suspect you'll see the exception thrown with a stack trace which includes CheckList, OnOCXEvent - and a bunch of code in-between, with something that runs the message loop in the middle.
Related
I have spent a few days now finding a bug that freezes my companies application. The dreaded UserPreferenceChanged UI freeze. It's not a complicated bug, but hard to find in a rather big application. There are quite a few articles about how this bug unfolds but not on how to put ones finger on the faulty code. I have put together a solution, in form of a logging mechanism from multiple older tickets and (i hope) improved a bit upon them. May it save some time for the next programmer with this problem.
How to recognize the bug?
The application freezes completely. Nothing more to be done than create a memory dump and then close it via TaskManager. If you open the dmp file in VisualStudio or WinDbg you might see a stack trace like this one
WaitHandle.InternalWaitOne
WaitHandle.WaitOne
Control.WaitForWaitHandle
Control.MarshaledInvoke
Control.Invoke
WindowsFormsSynchronizationContext.Send
System.EventInvokeInfo.Invoke
SystemEvents.RaiseEvent
SystemEvents.OnUserPreferenceChanged
SystemEvents.WindowProc
:
The important two lines here are "OnUserPreferenceChanged" and "WindowsFormsSynchronizationContext.Send"
What's the cause?
SynchronizationContext was introduced with .NET2 to generalize thread synchronization. It gives us methods like "BeginInvoke" and such.
The UserPreferenceChanged event is rather self explanatory. It will be triggered by the user changing his background, logging in or out, changing the Windows accent colors and lots of other actions.
If one creates a GUI control on a background thread a WindowsFormsSynchronizationContext is installed on said thread. Some GUI controls subscribe to the UserPreferenceChanged event when created or when using certain methods. If this event is triggered by the user the main thread sends a message to all subscribers and waits. In the described scenarion: a worker thread without a message loop! The application is frozen.
To find the cause of the freeze can be especially hard because the cause of the bug (creation of GUI element on a background thread) and the error state (application frozen) can be minutes apart. See this really good article for more details and a slightly different scenario. https://www.ikriv.com/dev/dotnet/MysteriousHang
Examples
How can one provoke this error for testing purposes?
Example 1
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
using (var r = new RichTextBox())
{
IntPtr p = r.Handle; //do something with the control
}
Thread.Sleep(5000); //simulate some work
}
Not bad but not good either. If the UserPreferenceChanged event gets triggered in the few milliseconds you use the RichTextBox your application will freeze. Could happen, not very likely though.
Example 2
private void button_Click(object sender, EventArgs e)
{
new Thread(DoStuff).Start();
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
Thread.Sleep(5000); //simulate some work
}
This is bad. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox does not get disposed. If the UserPreferenceChangedEvent occures while the thread lives your application will freeze.
Example 3
private void button_Click(object sender, EventArgs e)
{
Task.Run(() => DoStuff());
}
private void DoStuff()
{
var r = new RichTextBox();
IntPtr p = r.Handle; //do something with the control
}
This is a nightmare. Task.Run(..) will execute the work on a background thread on the threadpool. The WindowsFormsSynchronizationContext gets not cleaned up because the RichTextBox is not disposed. Threadpool threads are not cleaned up. This background thread now lurks in your threadpool just waiting for the UserPreferenceChanged event to freeze your application even long after your task has returned!
Conclusion: Risk is manageable when you know what you do. But whenever possible: avoid GUI Elements in a background thread!
How to deal with this bug?
I put together a solution from older tickets. Thanks very much to those guys!
WinForms application hang due to SystemEvents.OnUserPreferenceChanged event
https://codereview.stackexchange.com/questions/167013/detecting-ui-thread-hanging-and-logging-stacktrace
This solution starts a new thread that continuously tries to detect any threads which are subscribed to the OnUserPreferenceChanged Event and then provide a call stack that should tell you why that is.
public MainForm()
{
InitializeComponent();
new Thread(Observe).Start();
}
private void Observe()
{
new PreferenceChangedObserver().Run();
}
internal sealed class PreferenceChangedObserver
{
private readonly string _logFilePath = $"filePath\\FreezeLog.txt"; //put a better file path here
private BindingFlags _flagsStatic = BindingFlags.NonPublic | BindingFlags.Static;
private BindingFlags _flagsInstance = BindingFlags.NonPublic | BindingFlags.Instance;
public void Run() => CheckSystemEventsHandlersForFreeze();
private void CheckSystemEventsHandlersForFreeze()
{
while (true)
{
try
{
foreach (var info in GetPossiblyBlockingEventHandlers())
{
var msg = $"SystemEvents handler '{info.EventHandlerDelegate.Method.DeclaringType}.{info.EventHandlerDelegate.Method.Name}' could freeze app due to wrong thread. ThreadId: {info.Thread.ManagedThreadId}, IsThreadPoolThread:{info.Thread.IsThreadPoolThread}, IsAlive:{info.Thread.IsAlive}, ThreadName:{info.Thread.Name}{Environment.NewLine}{info.StackTrace}{Environment.NewLine}";
File.AppendAllText(_logFilePath, DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") + $": {msg}{Environment.NewLine}");
}
}
catch { }
}
}
private IEnumerable<EventHandlerInfo> GetPossiblyBlockingEventHandlers()
{
var handlers = typeof(SystemEvents).GetField("_handlers", _flagsStatic).GetValue(null);
if (!(handlers?.GetType().GetProperty("Values").GetValue(handlers) is IEnumerable handlersValues))
yield break;
foreach(var systemInvokeInfo in handlersValues.Cast<IEnumerable>().SelectMany(x => x.OfType<object>()).ToList())
{
var syncContext = systemInvokeInfo.GetType().GetField("_syncContext", _flagsInstance).GetValue(systemInvokeInfo);
//Make sure its the problematic type
if (!(syncContext is WindowsFormsSynchronizationContext wfsc))
continue;
//Get the thread
var threadRef = (WeakReference)syncContext.GetType().GetField("destinationThreadRef", _flagsInstance).GetValue(syncContext);
if (!threadRef.IsAlive)
continue;
var thread = (Thread)threadRef.Target;
if (thread.ManagedThreadId == 1) //UI thread
continue;
if (thread.ManagedThreadId == Thread.CurrentThread.ManagedThreadId)
continue;
//Get the event delegate
var eventHandlerDelegate = (Delegate)systemInvokeInfo.GetType().GetField("_delegate", _flagsInstance).GetValue(systemInvokeInfo);
//Get the threads call stack
string callStack = string.Empty;
try
{
if (thread.IsAlive)
callStack = GetStackTrace(thread)?.ToString().Trim();
}
catch { }
yield return new EventHandlerInfo
{
Thread = thread,
EventHandlerDelegate = eventHandlerDelegate,
StackTrace = callStack,
};
}
}
private static StackTrace GetStackTrace(Thread targetThread)
{
using (ManualResetEvent fallbackThreadReady = new ManualResetEvent(false), exitedSafely = new ManualResetEvent(false))
{
Thread fallbackThread = new Thread(delegate () {
fallbackThreadReady.Set();
while (!exitedSafely.WaitOne(200))
{
try
{
targetThread.Resume();
}
catch (Exception) {/*Whatever happens, do never stop to resume the target-thread regularly until the main-thread has exited safely.*/}
}
});
fallbackThread.Name = "GetStackFallbackThread";
try
{
fallbackThread.Start();
fallbackThreadReady.WaitOne();
//From here, you have about 200ms to get the stack-trace.
targetThread.Suspend();
StackTrace trace = null;
try
{
trace = new StackTrace(targetThread, true);
}
catch (ThreadStateException) { }
try
{
targetThread.Resume();
}
catch (ThreadStateException) {/*Thread is running again already*/}
return trace;
}
finally
{
//Just signal the backup-thread to stop.
exitedSafely.Set();
//Join the thread to avoid disposing "exited safely" too early. And also make sure that no leftover threads are cluttering iis by accident.
fallbackThread.Join();
}
}
}
private class EventHandlerInfo
{
public Delegate EventHandlerDelegate { get; set; }
public Thread Thread { get; set; }
public string StackTrace { get; set; }
}
}
Attention
1)This is a very ugly hack. It deals with threads in a very invasive way. It should never see a live customer system. I was already nervous deploying it to the customers test system.
2)If you get a logfile it might be very big. Any thread might cause hundreds of entries. Start at the oldest entries, fix it and repeat.(Because of the "tainted thread" scenario from Example 3 it might also contain false positives)
3)I am not sure about the performance impact of this hack. I assumed it would be very big. to my surprise it was almost not noteable. Might be different on other systems though
I have a C# desktop application.
In my code I am acquiring an image from a camera. I pass this image to a routine.
This is the 'syntax' of my code:
//in a routine
if (!isProcessingMotion)
{
try
{
isProcessingMotion = true;
//do stuff
}
finally
{
isProcessingMotion = false;
}
}
//////
//module variable:
private static bool isProcessingMotion = false;
The function is reached when an event is raised from the parent code.
The trouble is the 'isProcessingMotion = false is not always 'hit'. I have put a try-catch around the whole of the code but there is no error.
I do not want to use monitor or lock('a read only static object') as when I do the app grinds down to a slow process.
What should I be looking out for?
I presume what is happening is not that the finally block isn't reached, it is that a different thread might be changing the isProcessingMotion variable after that finally block has executed. That might happen when a second event is fired, but the first event hasn't finished processing the first request.
It seems that there are multiple accesses to your field at one.
Although you prefer not to use a lock, this seems like the perfect fix for your problem.
Either that or you'll have to change your application logic to not make multiple reads to that same variable.
Edit
Since you have to process them sequentially, i'd definitely go with a lock:
var processingLock = new object();
if (!isProcessingMotion)
{
lock (processingLock)
{
isProcessingMotion = true;
// Do stuff..
isProcessingMotion = false;
}
}
Let's say I have a form with two buttons (button1 and button2) and a resource object (r). The resource has its own locking and unlocking code to handle concurrency. The resource could be modified by any thread.
When button1 is clicked, its handler does some modifying of r itself and then calls _IndependentResourceModifierAsync() asynchronously which does some modifying of r in a spawned task. _IndependentResourceModifierAsync() acquires r's lock before doing this. Also because the handler is messing with r itself, it acquires r's lock too.
When button2 is clicked, it just calls _IndependentResourceModifierAsync() directly. It does no locking itself.
As you know, the handlers for the buttons will always execute on the main thread (except for the spawned Task).
There's two things that I want to guarantee:
If either button1 or button2 is clicked while the resource is locked by the main thread, an exception will be thrown. (Can't use a Monitor or Mutex because they are thread driven)
The nesting of locks from button1_Click() through _IndependentResourceModiferAsync() should not cause a deadlock. (Can't use a Semaphore).
Basically, I think what I'm looking for is a "stack-based lock" if such a thing exists or is even possible. Because when an async method continues after an await, it restores stack state. I did a lot of searching for anyone else who has had this problem but came up dry. That likely means I'm over-complicating things, but I am curious what people have to say about it. There might be something really obvious I'm missing. Many thanks.
public class Resource
{
public bool TryLock();
public void Lock();
public void Unlock();
...
}
public class MainForm : Form
{
private Resource r;
private async void button1_Click(object sender, EventArgs e)
{
if (!r.TryLock())
throw InvalidOperationException("Resource already acquired");
try
{
//Mess with r here... then call another procedure that messes with r independently.
await _IndependentResourceModiferAsync();
}
finally
{
r.Unlock();
}
}
private async void button2_Click(object sender, EventArgs e)
{
await _IndependentResourceModifierAsync();
}
private async void _IndependentResourceModiferAsync()
{
//This procedure needs to check the lock too because he can be called independently
if (!r.TryLock())
throw InvalidOperationException("Resource already acquired");
try
{
await Task.Factory.StartNew(new Action(() => {
// Mess around with R for a long time.
}));
}
finally
{
r.Unlock();
}
}
}
The resource has its own locking and unlocking code to handle concurrency. The resource could be modified by any thread.
There's a yellow flag. I find that a design where you protect resources (rather than have them protect themselves) is usually better in the long run.
When button1 is clicked, its handler does some modifying of r itself and then calls _IndependentResourceModifierAsync() asynchronously which does some modifying of r in a spawned task. _IndependentResourceModifierAsync() acquires r's lock before doing this. Also because the handler is messing with r itself, it acquires r's lock too.
And there's a red flag. Recursive locks are almost always a bad idea. I explain my reasoning on my blog.
There's also another warning I picked up regarding the design:
If either button1 or button2 is clicked while the resource is locked by the main thread, an exception will be thrown. (Can't use a Monitor or Mutex because they are thread driven)
That doesn't sound right to me. Is there any other way to do this? Disabling buttons as the state changes seems like a much nicer approach.
I strongly recommend refactoring to remove the requirement for lock recursion. Then you can use SemaphoreSlim with WaitAsync to asynchronously acquire the lock and Wait(0) for the "try-lock".
So your code would end up looking something like this:
class Resource
{
private readonly SemaphoreSlim mutex = new SemaphoreSlim(1);
// Take the lock immediately, throwing an exception if it isn't available.
public IDisposable ImmediateLock()
{
if (!mutex.Wait(0))
throw new InvalidOperationException("Cannot acquire resource");
return new AnonymousDisposable(() => mutex.Release());
}
// Take the lock asynchronously.
public async Task<IDisposable> LockAsync()
{
await mutex.WaitAsync();
return new AnonymousDisposable(() => mutex.Release());
}
}
async void button1Click(..)
{
using (r.ImmediateLock())
{
... // mess with r
await _IndependentResourceModiferUnsafeAsync();
}
}
async void button2Click(..)
{
using (r.ImmediateLock())
{
await _IndependentResourceModiferUnsafeAsync();
}
}
async Task _IndependentResourceModiferAsync()
{
using (await r.LockAsync())
{
await _IndependentResourceModiferUnsafeAsync();
}
}
async Task _IndependentResourceModiferUnsafeAsync()
{
... // code here assumes it owns the resource lock
}
I did a lot of searching for anyone else who has had this problem but came up dry. That likely means I'm over-complicating things, but I am curious what people have to say about it.
For a long time, it wasn't possible (at all, period, full-stop). With .NET 4.5, it is possible, but it's not pretty. It's very complicated. I'm not aware of anyone actually doing this in production, and I certainly don't recommend it.
That said, I have been playing around with asynchronous recursive locks as an example in my AsyncEx library (it will never be part of the public API). You can use it like this (following the AsyncEx convention of already-cancelled tokens acting synchronously):
class Resource
{
private readonly RecursiveAsyncLock mutex = new RecursiveAsyncLock();
public RecursiveLockAsync.RecursiveLockAwaitable LockAsync(bool immediate = false)
{
if (immediate)
return mutex.LockAsync(new CancellationToken(true));
return mutex.LockAsync();
}
}
async void button1Click(..)
{
using (r.LockAsync(true))
{
... // mess with r
await _IndependentResourceModiferAsync();
}
}
async void button2Click(..)
{
using (r.LockAsync(true))
{
await _IndependentResourceModiferAsync();
}
}
async Task _IndependentResourceModiferAsync()
{
using (await r.LockAsync())
{
...
}
}
The code for RecursiveAsyncLock is not very long but it is terribly mind-bending to think about. It starts with the implicit async context that I describe in detail on my blog (which is hard to understand just by itself) and then uses custom awaitables to "inject" code at just the right time in the end-user async methods.
You're right at the edge of what anyone has experimented with. RecursiveAsyncLock is not thoroughly tested at all, and likely never will be.
Tread carefully, explorer. Here be dragons.
I believe asynchronous re-entrant locking that behaves reasonably well is impossible. This is because when you start an asynchronous operation, you're not required to immediately await it.
For example, imagine you changed your event handler to something like this:
private async void button1_Click(object sender, EventArgs e)
{
if (!r.TryLock())
throw InvalidOperationException("Resource already acquired");
try
{
var task = _IndependentResourceModiferAsync();
// Mess with r here
await task;
}
finally
{
r.Unlock();
}
}
If the lock was asynchronously re-entrant, the code that works with r in the event handler and the code in the invoked asynchronous method could work at the same time (because they can run on different threads). This means such lock wouldn't be safe.
I think you should look at SemaphoreSlim (with a count of 1):
It isn't re-entrant (it's not owned by a thread)
It supports asynchronous waiting (WaitAsync)
I don't have time to check through your scenario right now, but I think it would fit.
EDIT: I've just noticed this bit of the question:
Because when an async method continues after an await, it restores stack state.
No, it absolutely doesn't. That's easily to show - add an async method which responds to a button click like this:
public void HandleClick(object sender, EventArgs e)
{
Console.WriteLine("Before");
await Task.Delay(1000);
Console.WriteLine("After");
}
Set a break point on both of your Console.WriteLine calls - you'll notice that before the await, you've got a stack trace including the "button handling" code in WinForms; afterwards the stack will look very different.
I have a Form which "listens" to events that are raised elsewhere (not on the Form itself, nor one of its child controls). Events are raised by objects which exist even after the Form is disposed, and may be raised in threads other than the one on which the Form handle was created, meaning I need to do an Invoke in the event handler (to show the change on the form, for example).
In the Dispose(bool) method of the form (overridden) I unsubscribed from all events that may still be subscribed when this method is called. However, Invoke is still called sometimes from one of the event handlers. I assume this is because the event handler gets called just a moment before the event is unsubscribed, then OS switches control to the dispose method which executes, and then returns control back to the handler which calls the Invoke method on a disposed object.
Locking the threads doesn't help because a call to Invoke will lock the calling thread until main thread processes the invoked method. This may never happen, because the main thread itself may be waiting for a release of the lock on the object that the Invoke-calling thread has taken, thus creating a deadlock.
So, in short, how do I correctly dispose of a Form, when it is subscribed to external events, which may be raised in different threads?
Here's how some key methods look at the moment. This approach is suffering the problems I described above, but I'm not sure how to correct them.
This is an event handler handling a change of Data part of the model:
private void updateData()
{
if (model != null && model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
model.Data.SomeDataChanged += new MyEventHandler(updateSomeData);
}
updateSomeData();
}
This is an event handler which must make changes to the view:
private void updateSomeData()
{
if (this.InvokeRequired) this.myInvoke(new MethodInvoker(updateSomeData));
else
{
// do the necessary changes
}
}
And the myInvoke method:
private object myInvoke(Delegate method)
{
object res = null;
lock (lockObject)
{
if (!this.IsDisposed) res = this.Invoke(method);
}
return res;
}
My override of the Dispose(bool) method:
protected override void Dispose(bool disposing)
{
lock (lockObject)
{
if (disposing)
{
if (model != null)
{
if (model.Data != null)
{
model.Data.SomeDataChanged -= new MyEventHandler(updateSomeData);
}
// unsubscribe other events, omitted for brevity
}
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
}
Update (as per Alan's request):
I never explicitly call the Dispose method, I let that be done by the framework. The deadlock has so far only happened when the application is closed. Before I did the locking I sometimes got some exceptions thrown when a form was simply closed.
There are two approaches to consider. One is to have a locking object within the Form, and have the internal calls to Dispose and BeginInvoke calls occur within the lock; since neither Dispose nor BeginInvoke should take very long, code should never have to wait long for the lock.
The other approach is to just declare that because of design mistakes in Control.BeginInvoke/Form.BeginInvoke, those methods will sometimes throw an exception that cannot practically be prevented and should simply be swallowed in cases where it won't really matter whether or not the action occurs on a form which has been disposed anyway.
I'd like to provide a sort of addendum to supercat's answer that may be interesting.
Begin by making a CountdownEvent (we'll call it _invoke_counter) with an initial count of 1. This should be a member variable of the form (or control) itself:
private readonly CountdownEvent _invoke_counter = new CountdownEvent(1);
Wrap each use of Invoke/BeginInvoke as follows:
if(_invoke_counter.TryAddCount())
{
try
{
//code using Invoke/BeginInvoke goes here
}
finally { _invoke_counter.Signal(); }
}
Then in your Dispose you can do:
_invoke_counter.Signal();
_invoke_counter.Wait();
This also allows you to do a few other nice things. The CountdownEvent.Wait() function has an overload with a timeout. Perhaps you only want to wait a certain period of time to let the invoking functions finish before letting them die. You could also do something like Wait(100) in a loop with a DoEvents() to keep things responsive if you expect the Invokes to take a long time to finish. There's a lot of niftyness you can achieve with this method.
This should prevent any weird timing race condition type of issues and it's fairly simple to understand and implement. If anyone sees any glaring problems with this, I'd love to hear about them because I use this method in production software.
IMPORTANT: Make sure that the disposal code is on the Finalizer's thread (which it should be in a "natural" disposal). If you try to manually call the Dispose() method from the UI thread, it will deadlock because it will get stuck on the _invoke_counter.Wait(); and the Invokes won't run, etc.
I had the problem with the Invoke method while multithreading, and I found a solution that works like a charm!
I wanted to create a loop in a task that update a label on a form to do monitoring.
But when I closed the form window, my Invoke threw an exception because my Form is disposed !
Here is the pattern I implemented to resolve this problem:
class yourClass : Form
{
private bool isDisposed = false;
private CancellationTokenSource cts;
private bool stopTaskSignal = false;
public yourClass()
{
InitializeComponent();
this.FormClosing += (s, a) =>
{
cts.Cancel();
isDisposed = true;
if (!stopTaskSignal)
a.Cancel = true;
};
}
private void yourClass_Load(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task.Factory.StartNew(() =>
{
try
{
while (true)
{
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate { methodToInvoke(); });
}
}
}
catch (OperationCanceledException ex)
{
this.Invoke((MethodInvoker)delegate { stopTaskSignalAndDispose(); });
}
}, token);
}
public void stopTaskSignalAndDispose()
{
stopTaskSignal = true;
this.Dispose();
}
public void methodToInvoke()
{
if (isDisposed) return;
label_in_form.Text = "text";
}
}
I execute methodToInvoke() in an invoke to update the label from the form's thread.
When I close the window, the FormClosing event is called. I take this opportunity to cancel the closing of the window (a.Cancel) and to call the Cancel method of the object Task to stop the thread.
I then access the ThrowIfCancellationRequested() method which throws an OperationCanceledException allowing, juste after, to exit the loop and complete the task.
The Invoke method sends a "Window message" in a Queue.
Microsoft says : « For each thread that creates a window, the operating system creates a queue for window messages. »
So I call another method that will now really close the window but this time by using the Invoke method to make sure that this message will be the last of the Queue!
And then I close the window with the Dispose() method.
I have an Exception Handling project in my application that can be called from anywhere to show the user there is a problem with with system. Everything works great when the call is made from somewhere in the UI as expected. When I make calls from no UI parts of the application everything freezes. I have the code wrapped in the thread safe calls and when stepping through they don't require the Invoke call. Any help is greatly appreciated. Code below:
Inside the form
void err_DispEvent(string text)
{
if (InvokeRequired)
{
Invoke(new Error.DisplayDelegate(err_DispEvent), new object [] {text});
}
else
{
this.Show();
}
}
Call from the class
public void FaultError(string errorMsg)
{
FaultForm fform = new FaultForm(errorMsg, "Internal Fault");
if (this.dispEvent != null)
{
dispEvent(errorMsg);
}
}
public event DisplayDelegate DispEvent
{
add { dispEvent += value; }
remove { dispEvent -= value; }
}
private event DisplayDelegate dispEvent;
public delegate void DisplayDelegate(string text);
Sample of how the class is used in the application
ECDUExceptions.Error newError = ECDUExceptions.Error.getInstance();
newError.FaultError("Heater is not responding to function calls, it has been turned off");
Some information when re-invoking methods:
Use BeginInvoke(...) instead of Invoke(...) as this will not wait for the call to finish, and so won't freeze the calling thread.
Use an Action when re-invoking. So in your case, you could change your invocation to:
BeginInvoke(new Action<string>(err_DispEvent), text);
Use BeginInvoke(...) instead of Invoke(...). THis will put your message request in the end of the queue
Create a queue of some sort for the messages that should be displayed.
Fill the queue from whatever thread you required.
From the GUI responsible for showing the messages, use timer to dequeue and show them.
Simplistic but will work effortlessly. And you won't need to Invoke() anything since Forms.Timer runs on UI message loop.