Windows forms with SerialPort - app hangs after closing form - c#

I have a windows form application in order to connect to Arduino board. when I want to close it, it stays open until I stop debugging mode. this happens when I running program in Visual Studio and when I run exe file alone, I have to stop it from task manager.
I've tried to both FormClosing and FormClosed events but the result is the same. the only thing that crossed my mind is that this problems occurs because I used many Invoke functions for my controls in the DataRecieved event of my SerialPort. I've done this because I need thread-safe calls for my form controls. Here is some part of my code:
private void spArduino_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
if (spArduino.BytesToRead > 0)
{
string data = spArduino.ReadLine().Replace("\r", "");
if (data.StartsWith("CUR_TEMP:"))
{
if (lbTemprature.InvokeRequired)
{
lbTemprature.Invoke(new MethodInvoker(delegate {
lbTemprature.Text = "Room temprature " + data.Remove(0,9) + "°C";
}));
}
}
}
}
///////
private void Monitoring_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
spArduino.WriteLine("CLEAR");
spArduino.Close();
}
catch (Exception)
{
MessageBox.Show("errorclose");
}
}
And this shows up in my output (Visual Studio)
The thread 0x2bf0 has exited with code 0 (0x0).
The thread 0x5f24 has exited with code 0 (0x0).
The thread 0x46c4 has exited with code 0 (0x0).
The thread 0x5df4 has exited with code 0 (0x0).
The thread 0x294c has exited with code 0 (0x0).
The thread 0x4620 has exited with code 0 (0x0).
The thread 0x720 has exited with code 0 (0x0).
The thread 0x35a0 has exited with code 0 (0x0).
And it stays this way until I stop the program.
Can anyone help me to understand where is my problem and how I can solve it?

The SerialPort.DataReceived event is triggered on a separate thread than the main UI thread, that's why you needed to call the lbTemprature.Invoke method when updating the UI.
Closing the form without closing the port
If you close the form without closing the port, then when closing the form, the DataReceived event may be triggered after the form disposed, which will cause an exception when trying to update the UI.
Closing the port before the form is closed
If you close the port before the form is closed (e.g. in the FormClosing event) than you may encounter a dead-lock because the SerialPort.Close() in the main UI thread waits for the thread that triggers the DataReceived event to complete the event, and the DataReceived event is waiting for the UI thread when you call lbTemprature.Invoke. This is probably what's causing the form to freez.
Solution
A solution can be calling lbTemprature.BeginInvoke to avoid the dead-lock. This may not be enough, because the BeginInvoke can still run after the form is disposed and cause an expectation. Adding a check to the Form.IsDisposed property maybe needed.
if (lbTemprature.InvokeRequired)
{
lbTemprature.BeginInvoke(new MethodInvoker(delegate
{
if (!this.IsDisposed)
{
lbTemprature.Text = "Room temprature " + data.Remove(0,9) + "°C";
}
}));
}

Look into how you are cleaning up the serial device. Try unsubscribing from the event(s) (ie. -= spArduino_DataReceived). It is possible that references are preventing garbage collection and keeping your form alive after closing.

Related

C# Thread not closing (appears to crash) when using delegate to announce data

So I have run into an odd situation. I have been using a pattern successfully for a while now to communicate data between 2 threads. Assume there are 2 threads:
Thread A (GUI thread)
Thread B (polling a piece of equipment in the background for data)
Thread B is executing the PollStatus function then sleeping the thread for a wait period. When it gets the data, it raises a delegate event in Thread A, which marshals back to Thread A to update some labels with the data.
Thread A Code:
private delegate void LabJackUSB_OnDataAvailableCallback(LabJackRef.Status LabJackStatus);
private void LabJackUSB_OnDataAvailable(LabJackRef.Status LabJackStatus)
{
if (this.InvokeRequired)
{
this.Invoke(new LabJackUSB_OnDataAvailableCallback(LabJackUSB_OnDataAvailable), new object[] { LabJackStatus });
}
else
{
// Do something with data here
}
}
Thread B Code:
public delegate void DataAvailable(LabJackRef.Status LabJackStatus);
public event DataAvailable OnDataAvailable;
private bool PollStatus(StepDefinition CurrentStep)
{
GetData();
AnnounceDataAvailable();
return false;
}
private void AnnounceDataAvailable()
{
if (OnDataAvailable != null)
{
OnDataAvailable(USBStatus);
}
}
The thread is a while loop that executes the step, then sleeps the thread. The while loop continues until the state machine has performed its closing steps and sets the WorkerState.StateMachineComplete flag to true, at which point the while loop exits and the thread should close.
protected void Worker(object Worker)
{
while (true)
{
// see if we are waiting to close the thread
if (((ThreadInfo)Worker).CloseThread)
{
// set flag to exit worker when complete
WorkerState.ThreadClosing = true;
// if we have finished closing the state machine,
// we can exit the while loop to close the thread
if (WorkerState.StateMachineComplete)
break;
// otherwise, jump to the last step
StateMachine.JumpToLast(WorkerStateMachine);
}
// run the state machine
StateMachine.ExecuteStep(WorkerStateMachine, WorkerState);
// wait for the time defined in the current step
((ThreadInfo)Worker).WaitHandle.WaitOne(WorkerState.DelayTimeMS);
}
}
Now this has been working well in multiple cases (no issues). For some reason when I am us the announce function it will not close, but no break points inside the while loop will stop the debugger. To me that means the while loop is no longer looping, but I don't know how that could be. I am not getting any unhandled exceptions in my application, so no clues there.
If I just comment out the announce function, the thread closes no problem, so I know the problem is not in the code to get the data or set the labels.
If I comment out the Thread A portion that does anything with the data, the error is still there, but if I don't subscribe to the event in Thread A, it will close properly.
Honestly I am at a loss on even how to start debugging this since the debugger is not being able to stop anywhere in the loop that is waiting to close.
Any ideas would be appreciated!
Thanks,
John Vickers

My program would close after opening my next form

My second form won't open, willing to give more details if needed
After clicking on the button, it will proceed to the next form but it just closes before it there
private void btnNext_Click(object sender, EventArgs e)
{
frmGame_3_ v = new frmGame_3_();
this.Close();
v.Show();
}
This is what shows when the program closes on me.
The thread 0x21fc has exited with code 259 (0x103). The thread 0x22b8
has exited with code 259 (0x103). 'ICSCulminating.vshost.exe' (CLR
v4.0.30319: ICSCulminating.vshost.exe): Loaded
'C:\Users\Owner\Desktop\ICSCulminating(David)e3\ICSCulminating\bin\Debug\ICSCulminating.exe'.
Symbols loaded. The thread 0x207c has exited with code 259 (0x103).
The thread 0x23c4 has exited with code 259 (0x103). The program
'[7568] ICSCulminating.vshost.exe' has exited with code 0 (0x0).
You can, as suggested by commenter adv12, hide the form instead of closing it. This avoids the problem of the closing of the main form causing your event loop to terminate and the program to exit.
However, it means you are leaving the unused form around, undisposed. IMHO, even if this is not a serious problem per se, it would be better to fix the issue in a cleaner way.
That is, change your program's Main() method so that it uses the parameter-less overload of the Application.Run() method. This removes the dependency on the initial form so that you can close it without the whole program exiting. Then, when you to want to close the program, you can call the Application.ExitThread() method (or Application.Exit() if you prefer).
In your Main() method (found in the Program.cs file of your project), change the last line of the method, which will look something like this:
Application.Run(new Form1());
…to this:
new Form1().Show();
Application.Run();
Note: the name of your main Form subclass may be something other than Form1. Obviously, use whatever class name is in your own code now, if it's not Form1.

C#: inner workings: events, Control.BeginInvoke and program exiting = Interruption?

I'm creating a program with several projects and some projects report back to the main project messages for logging purposes.
Since i am using Asynch sockets, some of these messages come back in different threads, so once they get to the main thread i check InvokeRequired and if true i use this.BeginInvoke to handle the logging.
I need to handle it in the UI thread specially in the case of the server, where i show the last logged messages on a ListBox so i can follow the progress of operations during my tests.
I know sometimes it may happen that a few messages get switched around, but for now i can live with that. For some reason if i use Invoke instead of BeginInvoke, the server will crash if i stop it while clients are connected, and won't even give any exception. But using BeginInvoke i overcame this.
My question regards understanding how events and BeginInvoke work in case of program termination. If an event is on queue or a BeginInvoke has been called just before the program is closed, will it terminate imediatly, cancelling everything? or will it perform all pending actions, in my case log the pending message, and then exit?
You'll have to delay closing the form if you want to ensure all BeginInvoked delegates are executed. You can do so by making it a two-step process, appending another BeginInvoke delegate to the queue that actually closes the form. Like this:
private bool closing;
protected override void OnFormClosing(FormClosingEventArgs e) {
if (!closing) {
closing = true;
// Do your stuff
//...
this.BeginInvoke(new Action(() => this.Close()));
e.Cancel = true;
}
base.OnFormClosing(e);
}
When you call BeginInvoke to update UI, the code will be executed by a thread from the threadpool. And if the code raises an exception, it will only terminate the thread, not the whole application. That's why you have seen that your program didn't crash.
When BeginInvoke had just been called, and the program was terminated immediately. The remaining operations (logging ) won't be executed, because the thread from the threadpool

C#.NET Threading Question

I am facing an issue with communication between threads in a C#.NET application.
Hope someone will guide me in the right direction about the possible solutions.
I have an application in C#.NET.It is a windows form application.
My application has two threads - One thread is the main thread (UI thread) and the other one is the child thread. Lets call the child thread the "workerThread"
There is only one form used in the application.Lets call this form the "MainForm"
The child thread is started when the MainForm loads (used the form's "Load" event handler to start the thread)
In the MainForm class, I have a variable named "stopWork" which is a public boolean variable and it serves as a flag to indicate whether the child thread should continue working or should it stop
I have another class (besides the MainForm class) which contains the method that I execute in the the child thread. Lets call this second class the "WorkerClass".
I pass a reference to the current form (the MainForm) into the constructor of the "WorkerClass"
I have a button "stop" in the main form which sets "stopWork" to "true" if its clicked and then calls "workerThread.Join()" to wait for the child thread to finish excecution.
In the child thread, the method "doWork" keeps checking the status of "parentForm.stopWork" inside a for loop. If "stopWork" is set to "true" then the loop breaks and subsequently the method ends.
Now, the issue is, once I am clicking the "stop" button ,the application hangs.
I am pasting parts of the code below so that it is easier to understand :
public partial class MainForm : Form
{
Thread workerThread = null;
ThreadStart workerThreadStart = null;
WorkerClass workerClass = null;
public bool stopWork = true;
/*.......... some code ............*/
private void MainForm_Load(object sender, EventArgs e)
{
workerThreadStart = new ThreadStart(startWork);
workerThread = new Thread(workerThreadStart);
stopWork = false;
workerThread.Start();
}
private void startWork()
{
workerClass = new WorkerClass(this);
}
private void buttonStop_Click(object sender, EventArgs e) //"stop" button
{
if (workerThread != null)
{
if (workerThread.IsAlive == true)
{
stopWork = true;
workerThread.Join();
}
}
}
/*.......... some more code ............*/
}
public class WorkerClass
{
MainForm parentForm=null;
/*......... some variables and code ........*/
public WorkerClass(MainForm parentForm)
{
this.parentForm=parentForm;
}
/* .............. some more code ...........*/
public void doWork()
{
/*.......... some variables and code ...........*/
for(int i=0;i<100000;i++)
{
// ** Here is the check to see if parentForm has set stopWork to true **
if(parentForm.stopWork==true)
break;
/*......... do some work in the loop ..........*/
}
}
/********* and more code .........*/
}
I think I may know where the problem lies.
The problem is in the "doWork" method in the child thread trying to access "stopWork" variable in the parent form when already the parent form is blocked by calling the "workerThread.Join()" method. So ,I think this is a "deadlock" problem.
Am I right in identifying the problem ? Or am I wrong and the problem lies somewhere else ?
In case this is indeed a deadlock, what are the possible solutions to solve this ?
I did a bit of googling and found lots of resources on thread synchronisation and how to avoid deadlocks. But I could not understand how to apply them specifically to my problem.
I would really appreciate any help or guidance on resolving this issue.
Yes, the code you wrote is highly vulnerable to deadlock. The BackgroundWorker class is especially prone to cause this kind of deadlock.
The problem is located in code we can't see in your snippet, the WorkerClass. You are surely doing something there that affects the UI in one way or another, always the primary reason to consider creating a thread in the first place. You probably use Control.Invoke() to have some code run on the UI thread and update a control. Perhaps also to signal that the worker thread is completed and, say, set the Enable property of a button back to true.
That's deadlock city, such code cannot run until the UI thread goes idle, back to pumping its message loop. It will never be idle in your case, it is stuck in Thread.Join(). The worker thread can't complete because the UI thread won't go idle, the UI thread can't go idle because the worker thread isn't finishing. Deadlock.
BackgroundWorker has this problem too, the RunWorkerCompleted event cannot run unless the UI thread is idle. What you need to do is not block the UI thread. Easier said than done, BGW can help you get this right because it runs an event when it completes. You can have this event do whatever you now do in the code past the Thread.Join() call. You'll need a boolean flag in your class to indicate that you are in the 'waiting for completion' state. This answer has relevant code.
Use a BackgroundWorker for this task instead. When you want to stop the task's execution, call the background worker's CancelAsync method.
Generally speaking, rolling your own threading code (on any platform) is a recipe for disaster if you don't have an expert-level understanding of multithreading (and even then it's still dangerous).

Cross-thread event handling in C#

I am working with a framework that runs its own event dispatcher in a separate thread. The framework may generate some events.
class SomeDataSource {
public event OnFrameworkEvent;
void FrameworkCallback() {
// This function runs on framework's thread.
if (OnFrameworkEvent != null)
OnFrameworkEvent(args);
}
}
I want to deliver these events to a Winforms object on Winforms thread. I obviously check for InvokeRequired and dispatch it to Winforms thread if necessary.
class SomeForm : Form {
// ...
public void SomeAction(SomeArgs args) {
if (InvokeRequired) {
BeginInvoke(new Action(SomeAction), args);
return;
}
// ...
}
}
Now events may be delivered when the form is in the process of being closed, which causes all sorts of problems, so I unregister the form's event handler from framework's event source on Winforms thread like this:
var form = new SomeForm();
var src = new SomeDataSource();
// ...
src.OnFrameworkEvent += form.SomeAction;
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
Now, is this approach thread-safe? If the form is in the process of being closed, and a foreign thread calls BeginInvoke, will the invocation still be queued for execution if the form is closed? (which means I still have a chance of encountering the same problem)
Is there a better approach or recommended pattern for cross-thread event handling?
No it is not. The thread might just be executing the event handler while you unregister it and close the form. Small odds, but not zero. You have to have the thread stopped before you can close the form. If you don't want to abort it, you'll have to keep the form open by canceling the FormClosing event, then let the thread's completion callback close the form.
Check this thread for more info.
You can add this code to constructor CheckForIllegalCrossThreadCalls = false; and no exception will be thrown.
I haven't used a framework with its own event dispatcher, but I had my own experience with the threads that I created. Here's my experience
This approach is not thread-safe. The invocation will still be called even if the program itself is closed. I saw this in task manager (after the program is closed as you say) as hanging threads. (even if you kill the program from task manager also.). I had to kill those threads seperately later.
When the form is closing you have to kill the dispatcher thread so that it does not hang if anything wrong happens in that thread.
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
// pseudo-code (find c# equivalent)
if (dispatcherthread.isrunning)
dispatcherThread.kill();

Categories