Context: I'm writing a .NET library that communicates with a POS device over a serial port. I have to send specific commands over the serial port and then wait for a maximum amount of time for the device to answer back.
As I understand, the SerialPort object in .NET uses an event handler to signal when bytes are returned over the wire and this event handler is executed on a separate thread.
My plan is to use one main thread to manage the communication with the device, waiting for bytes to be received, parsing, responding, etc, and have the event handler for SerialPort.DataReceived
somehow "signal" back to the thread on which the communication is being managed, when that thread has to wait for bytes from the device. A CancellationToken
might also be involved on the main thread, in case the user decides to cancel the operation.
So I need:
- to make one thread (the main communication logic one) wait...
- ...until either a second thread (the one on which the
DataReceived
event handler fires) does some work or until a timeout occurs.
Initially I tried using Monitor.Pulse
and Monitor.Wait
for this, but I realized that Monitor.Wait
actually never returns if another thread does not release the lock, so I can end up with my main thread waiting forever (let's say the communication fails, etc).
After that, I started looking at EventWaitHandle
for this, but I'm not sure if it's the right choice. My plan now is as follows:
- Create a
new EventWaitHandle(false, EventResetMode.AutoReset)
- When waiting for bytes, on the main thread, do a
WaitHandle.WaitAny(new WaitHandle[] { _eventWaitHandle }, timeoutMilliseconds);
- When bytes are received, on the event handler thread, do a
_eventWaitHandle.Set();
This should also handle, I think, the case when, between sending some bytes and starting to wait for bytes to be received, some bytes are already received on the second thread as, if I understand correctly, WaitHandle.WaitAny
returns successfully right away if the EventWaitHandle
instance is already signaled.
I'd really like to know if I'm making any incorrect assumptions here about my plan, as multi-threaded programming is really not something I'm knowledgeable about.
EDIT: Since I realized that it was not initially clear, what I'm really asking is whether using EventWaitHandle
and WaitHandle.WaitAny
is a correct way to have one thread wait for a second thread to signal to it that it has done some work (within a specified timeframe), and then the waiting thread can continue its work.
LATER EDIT: Specifically for SerialPort
, I did look at avoiding having to work with thread synchronization and using async/await
via SerialPort.BaseStream
, but seems like that basically would involve reimplementing what's already available via the provided events, especially since ReadAsync
does not really cancel on timeout via a CancellationToken
unless the passed token is already canceled, (found this on other related questions and verified myself) which kinda defeats the purpose of using it at all, for me.
I ended up deciding to wrap my "control" thread in some async/await
code and then synchronizing it with the thread created by SerialPort
via AutoResetEvent
, since there's just two threads that need to be synchronized and it changes status automatically. Thanks for pointing out that it is avaialble!