multithreading - C# - How to hand off which thread reads from serial port? -
background
a customer asked me find out why c# application (we'll call xxx, delivered consultant has fled scene) flaky, , fix it. application controls measurement device on serial connection. device delivers continuous readings (which displayed on screen), , app needs stop continuous measurements , go command-response mode.
how not it
for continuous measurements, xxx uses system.timers.timer background processing of serial input. when timer fires, c# runs timer's elapsedeventhandler using thread pool. xxx's event handler uses blocking commport.readline() several second timeout, calls delegate when useful measurement arrives on serial port. portion works fine, however...
when time stop realtime measurements , command device different, application tries suspend background processing gui thread setting timer's enabled = false. of course, sets flag preventing further events, , background thread waiting serial input continues waiting. gui thread sends command device, , tries read reply – reply received background thread. background thread becomes confused not expected measurement. gui thread meanwhile becomes confused didn't receive command reply expected. know why xxx flaky.
possible method 1
in similar application, used system.componentmodel.backgroundworker thread free-running measurements. suspend background processing did 2 things in gui thread:
- call
cancelasyncmethod on thread, and - call
commport.discardinbuffer(), causes pending (blocked, waiting) comport read in background thread throwsystem.io.ioexception "the i/o operation has been aborted because of either thread exit or application request.\r\n".
in background thread catch exception , clean promptly, , works intended. unfortunately discardinbuffer provoking exception in thread's blocking read not documented behavior anywhere can find, , hate relying on undocumented behavior. works because internally discardinbuffer calls win32 api purgecomm, interrupts blocking read (documented behavior).
possible method 2
directly use baseclass stream.readasync method, monitor cancellation token, using supported way of interrupting background io.
because number of characters received variable (terminated newline), , no readasyncline method exists in framework, don't know if possible. process each character individually take performance hit (might not work on slow machines, unless of course line-termination bit implemented in c# within framework).
possible method 3
create lock "i've got serial port". nobody reads, writes, or discards input port unless have lock (including repeating blocking read in background thread). chop timeout values in background thread 1/4 second acceptable gui responsiveness without overhead.
question
does have proven solution deal problem? how can 1 cleanly stop background processing of serial port? i've googled , read dozens of articles bemoaning c# serialport class, haven't found solution.
thanks in advance!
msdn article serialport class states:
if
serialportobject becomes blocked during read operation, do not abort thread. instead, either close base stream or dispose ofserialportobject.
so best approach, point of view, second one, async reading , step step checking line-ending character. you've stated, check each char big performance loss, suggest investigate readline implementation ideas how perform faster. note use newline property of serialport class.
i want note there no readlineasync method default as msdn states:
by default,
readlinemethod block until line received. if behavior undesirable, setreadtimeoutproperty non-zero value forcereadlinemethod throwtimeoutexceptionif line not available on port.
so, may be, in wrapper can implement similar logic, task cancel if there no line end in given time. also, should note this:
because
serialportclass buffers data, , stream contained inbasestreamproperty not, 2 might conflict how many bytes available read.bytestoreadproperty can indicate there bytes read, these bytes might not accessible stream contained inbasestreamproperty because have been bufferedserialportclass.
so, again, suggest implement wrapper logic asynchronous read , checking after each read, there line-end or not, should blocking, , wrap inside async method, cancel task after time.
hope helps.
Comments
Post a Comment