I'm experiencing a weird behavior while trying to stop a SerialPort: the DataReceived event continues to fire after unsubscribing and after calling close! (see StopStreaming in the following code). As a result, in my event handler code I get an InvalidOperationException with the message that "The port is closed".
What am I missing? What is the correct way to close the port and stop the events?
EDIT: I get this error every time I run my code. So this is not a race condition that happens randomly but rather a systematic problem indicating a completely broken code! However, I fail to see how...
private SerialPort comPort = new SerialPort();
public override void StartStreaming()
{
comPort.Open();
comPort.DiscardInBuffer();
comPort.DataReceived += comPort_DataReceived;
}
public override void StopStreaming()
{
comPort.DataReceived -= comPort_DataReceived;
comPort.Close();
isStreaming = false;
}
private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType == SerialData.Chars)
{
SerialPort port = (SerialPort)sender;
int N = comPort.BytesToRead;
for (; N > 0; N--)
{
byte b = Convert.ToByte(comPort.ReadByte());
//... process b
}
}
}
EDIT: following the suggestions, I changed StopStreaming code to something like this:
public override void StopStreaming()
{
comPort.DataReceived -= comPort_DataReceived;
Thread.Sleep(1000);
comPort.DiscardInBuffer();
Thread.Sleep(1000);
comPort.Close();
isStreaming = false;
}
It seems to work now but I'm not really that happy. I wish there was a more effective way to remove the callback rather than inserting sleep periods in the program.
Your DataReceived event handler is called on a threadpool thread. And yes, they've got the awkward habit of running your code at an unpredictable time, it is not instant. So it is fairly inevitable that, if the device is actively sending data, that it can race with your Close() call and run after you closed it. Unsubscribing doesn't fix it, the threadpool thread already got its target method.
Do realize what you are doing to trigger this problem, you are closing the port while the device is sending data. That's not great, it is guaranteed to cause data loss. But not unlikely to happen when you are debugging your code since you don't actually care about the data.
A counter-measure is to turn off handshaking so the device cannot send anything anymore. Discard the input buffer. Then sleep for a while, a second or two, to ensure that any threadpool threads in-flight have completed running. Then close the port. A very pragmatic one is to simply not close the port, Windows will take care of it when your process terminates.
Looks like multi-threading issue.
SerialPort.DataReceived is raised in a worker thread from thread pool. You're closing port from the thread, that differs from the thread, where SerialPort.DataReceived raised in.
You can handle InvalidOperationException or write some synchronization code to solve this problem.
Update.
The problem is that if your devices sends data intensively, SerialPort queues more and more work items to thread pool. Even if your code will sleep any time before Close, it can be not enough. Unfortunately, SerialPort has an ugly implementation. It hasn't an option, which could tell "please, stop spam me with your data".
Hence, the concrete solution depends on device's protocol and handshaking parameters.
I had the same problem in an application I've been working on. It's exciting to read here about how the threadpool can bring it about.
Before I tracked down it's source though, I found that enclosing the contents of the DataReceived event handler in a try catch statement written in anticipation of the problem was a very effective way to solve it. Now that I know there's not really anything I can do to prevent the issue if I need/want to close a SerialPort while still receiving data, I'm quite happy with this approach.
I had similar issue when the user attempted to Exit application whilst it was still receiving data from the connected device. Application was throwing a System.IO.IOException following call to Me.Close().
Simplest solution I found was to set the SerialPort ReceivedBytesThreshold to a large number in the _FormClosing event handler. This reduces the frequency of DataReceived events and provides time for the Close() call to complete whilst the DataReceived event handler is inactive.
Related
I have an app, what have a few methods. They're basically button click events and timer events. When I click on a button or a few seconds passed, the timer or the button sends a message (I think TCP message) and instantly wait for an answer from another app. I use StreamWriter.Write() and StreamReader.Read() for the messages and answers. (They creates and accepts TCP string messages, I think).
But there are also unexpected messages, what can arrive anytime to my app. I don't know how to receive them.
Idea 1: after every StreamReader.Read(), prepare for the case when an unexpected message can come. But it seems to be a bad idea. Although the sending and reading messages happens very often, modifying the code in every place seems to be a bit weird solution.
Idea 2: using of a thread. It would accept every incoming message with StreamReader.Read(). But in my main thread, I would need to remove the StreamReader.Read() methods and simply wait for the answers from the other thread. This would happen with while(I don't get the answer){}, and this also seems to be a bad solution.
Idea 3: Using an eventhandler? I have never used any for this, so I'm a bit confused about how to use events for this task.
Any other idea?
Is your app allowed to read answer after a delay ? If I got it well, your app can send messages to something with button and can receive messages from it, sometimes from that request, sometimes spontaneously.
The immediate thing, maybe not the best, which comes to my mind is a polling peek thread.
Create a thread which loop until a condition is met (like shutting down communication), and which raise events, and wait for acknowledge
while(active){
if(yourStreamReader.Peek()>0) { //Blocking until data received
MyNewDataEvent?.Invoke(this, EventArgs.Empty); //Event raising
Acknowledge.WaitOne(); //Wait an acknowledge
}
}
And use a Callback on this raised event.
event EventHandler MyNewDataEvent;
ManualResetEvent Acknowledge;
//...
//...
MyNewDataEvent += OnNewData;
Acknowledge = new ManualResetEvent();
//...
//...
void OnNewData(object sender, EventArgs e){
//Read the messages, do stuff
Acknowledge.Set(); //When done with StreamReader
}
The cool effect is that it's also working for the answers from the button-sent requests, so you don't even have to read immediately the answer. That would be a good exercise to learn about event and eventhandler, which are, to me, REALLY useful ! Warning : Be careful that only one thread/callback use the streamreader at once.
I am porting an existing app from Borland C++ to .NET. Application handles 4 COM Ports simultaneously, i need to synchronize them, so that whilst one port is receiving data, the other three would block until one reads all the data in the receive buffer.
Requirements are, that new version works exactly in the same way as the previous one, so i need to find a way how to synchronize those 4 ports.
P.S.
I have got 4 instances of SerialPort class.
Below is a handler for receiving data over the COM port.
private void SerialPort_DataReceived( object sender, SerialDataReceivedEventArgs e )
{
SerialPort rThis = (SerialPort)sender;
string existingData = rThis.ReadExisting();
int NumReceived = existingData.Length;
if (NumReceived > 0)
{
char[] ReceivedByte = existingData.ToCharArray();
// if RX bytes cannot be processed
if (!rThis.ProcessReceivedBytes(ReceivedByte, NumReceived))
{
rThis.ReportThreadError(ThreadId.TI_READ, 0x07FFFFF);
}
}
}
Best thing is you have only one thread interacting with the ports, because this way you can't interact with the other ports while the thread is busy. This is exactly what you want, forget about multi-threading here.
Then, you should separate that low-level I/O thread from the GUI thread. So you'll end up with two threads that comunicate with one another over a well-defined API.
The low-level I/O thread requires a way of polling the serial ports without blocking, something like this:
while(polling) // GUI thread may interrupt polling on user request
{
foreach(SerialPort port in serialports)
{
if(port.HasDataToRead) // this is the polling you really need
{
// read data from port and handle it accordingly
}
}
// ... suspend thread now and then to prevent loop from consuming CPU time
}
The HasDataToRead should be implemented in the event handler, meaning:
catch in the event handler the event data is available and signal it with HasDataToRead inside the SerialPort class;
don't read the actual data in the event handler, event handlers often run on the GUI thread, you don't want to lock up the GUI;
at the end of the read method clear the HasDataToRead flag.
The cycle above really is a dispatcher, while the events are only used to orchestrate the flags inside the SerialPort instances.
Pay attention to the HasDataToRead flag, you'll have to lock it to avoid race conditions:
lock(HasDataToRead)
{
// access HasDataToRead
}
I have a serial port receiving data from an embedded device asynchronously. The user has the option to terminate the connection with the port at any time but this means that I get an exception if the user disconnects mid-transmission (sometimes the program just halts on myPort.Close()). Is there a way I can add a 'wait until empty' command? Something like this below?
private void tsDisconnect_Click(object sender, EventArgs e)
{
try
{
while(myPort.BytesToRead > 0)
{//wait here}
}
myPort.Close();
}
Waiting doesn't fix the deadlock you get from calling Close(), you'll need to fix the core problem. It is almost always caused by the code in your DataReceived event handler. We cannot see it, but a very common mistake is to use Control.Invoke() or Dispatcher.Invoke().
A serial port cannot close until the event handler returns. But it can't return because it is stuck in the Invoke() call. Which cannot complete because your UI thread is busy, it is stuck in the Close() call. Or is waiting for BytesToRead to get to 0, the solution you are pursuing. Won't work either, your DataReceived event handler is stuck and not reading anymore. Deadlock city.
You'll need to fix that code and use BeginInvoke() instead. That's a non-blocking call, it can't cause deadlock.
There are other possible reasons for deadlock. Easy to diagnose, you've got lots of time to debug it :) Use Debug + Break All, then Debug + Windows + Threads. Double-click the Main thread and look at its call stack to see what the UI thread is doing.
I am writing a program to simulate a device that transmits data over the serial port. To do this, I created a System.IO.Ports.SerialPort object in my form, and a System.Windows.Forms.Timer to do the transmitting at a given frequency. Everything works fine except that as the frequency approaches the limit of the serial port speed, it starts to lock up the UI and eventually becomes unresponsive when the data is being sent for transmission faster than the port data speed. My code is:
private void OnSendTimerTick(object sender, EventArgs e)
{
StringBuilder outputString = new StringBuilder("$", 51);
//code to build the first output string
SendingPort.WriteLine(outputString.ToString());
outputString = new StringBuilder("$", 44);
//code to build the second output string
SendingPort.WriteLine(outputString.ToString());
if (SendingPort.BytesToWrite > 100)
{
OnStartStopClicked(sender, e);
MessageBox.Show("Warning: Sending buffer is overflowing!");
}
}
I was expecting the WriteLine function to be asynchronous - return immediately while the port transmits in the background. Instead, it seems that the OnSendTimerTick function is properly threaded, but WriteLine seems to be running in the UI thread.
How can I get the serial port to behave in this way? Creating the SerialPort object in the timer seems like a bad idea, because then I'd have to open and close it on each timer tick.
It is only partly asynchronous, it will immediately return but only as long as the bytes you write will fit in the serial port driver's transmit buffer. That's going to come to a screeching stop when you flood the port with too much data, faster than it can transmit. You can make it truly asynchronous by using the SerialPort.BaseStream.BeginWrite() method. That doesn't make it any faster but moves the bottleneck somewhere else, possibly away from the UI.
If you're using System.Windows.Forms.Timer, the timer event handler is being executed on the UI thread. Same is true if you're using System.Timers.Timer when the SynchronizingObject is set to the form. If the serial port buffer fills up, the thread has to wait until it has enough space to hold the new data that you want to send.
I would suggest that you use a System.Threading.Timer to do this. The timer callback is called on a pool thread, meaning that the UI thread won't lock up if the WriteLine has to wait. If you do this, then you have to make sure that there is only one threading executing the timer callback at any time. Otherwise you can get data out of order. The best way to do that would be to make the timer a one-shot and re-initialize it at the end of every callback:
const int TimerFrequency = 50; // or whatever
System.Threading.Timer timer;
void InitTimer()
{
timer = new System.Threading.Timer(TimerCallback, null, TimerFrequency, Timeout.Infinite);
}
void TimerCallback(object state)
{
// do your stuff here
// Now reset the timer
timer.Change(TimerFrequency, Timeout.Infinite);
}
Passing a valid of Timeout.Infinite as the period parameter prevents the timer from being a periodic timer. Instead, it fires just once. The Timer.Change re-initializes the timer after each send.
A possibly better way to handle this is to eliminate the timer altogether by setting the WriteBufferSize to a sufficiently large value. Then your program can just dump all of its data into the buffer and let the SerialPort instance worry about dribbling it out across the wire. This assumes, of course, that you can create a buffer large enough to hold whatever your program is trying to send.
This could be resolved(slowness of the UI) if you created a Queue of strings to be written and had a background thread that wrote to the serial port from the queue. If you take that approach be careful of the size of the queue.
edit: For some reason I can't use Add Comment, so I'll just edit this. The documentation for BeginWrite has this statement "The default implementation of BeginWrite on a stream calls the Write method synchronously, which means that Write might block on some streams." It then goes on to exclude File and Network streams, but not SerialPort. I guess you can try it and see.
Whenever i am updating UI in windows form using delegate it gives me cross thread exception
why it is happening like this?
is there new thread started for each delegate call ?
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
clsConnect(statusMsg);
}
protected void displayResponse(string resp)
{
//here cross thread exception occur if directly set to lblMsgResp.Text="Test";
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.Invoke(new MethodInvoker(delegate { lblMsgResp.Text = resp; }));
}
}
The DataReceived event is always raised on a threadpool thread. You cannot update any UI control, you have to use Control.BeginInvoke(). There is no point testing InvokeRequired, it is always true.
A couple of things to keep in mind here:
Don't call Control.BeginInvoke for every single character or byte that you receive. That will bring the UI thread to its knees. Buffer the data you get from the serial port until you've got a complete response. Using SerialPort.ReadLine() usually works well, a lot of devices send strings that are terminated by a line feed (SerialPort.NewLine).
Shutting down your program can be difficult. You have to make sure to keep the form alive until the serial port stops sending. Getting an event after the form is closed will generate an ObjectDisposed exception. Use the FormClosing event to close the serial port and start a one second timer. Only really close the form when the timer expires.
Avoid using Control.Invoke instead of BeginInvoke. It can deadlock your program when you call SerialPort.Close().
Lots of ways to get in trouble. Consider using your own thread instead using DataReceived to avoid them.
Port_DataReceived is obviously an async event handler that is being raised by a thread on port monitoring component.
is there new thread started for each
delegate call ?
No, probably not. Your port monitoring component is running the poll on a background thread and the event is being raised from that thread, every time.
The point is that it is being called on a thread other than the UI, so you will need to use Control.Invoke and the patterns associated with it.
Consider this, (and read the post that may illuminate things for you)
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
UpdateTheUI(statusMsg);
}
private void UpdateTheUI(string statusMsg)
{
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.BeginInvoke(new MethodInvoker(UpdateTheUI,statusMsg));
}
else
{
clsConnect(statusMsg);
}
}
With all of that said, I would be remiss if I didn't point out that the indirection is troubling.
Cross Thread exception happens when some none UI thread changes UI elements. Since UI elements should be changed only in the UI thread this exception is thrown. To help you understand why this happen you will have to publish you code.
Cross thread exception happens when some none UI thread changes the UI elements. To fix this use the Invoke method on the control itself. As an extra you can check InvokeRequired on the control before calling the Invoke method
See msdn