背景
一位客戶問我爲什麼他們的C#應用程序(我們稱之爲XXX,由一名已經逃離現場的顧問提供)很片面,並修復它。該應用程序通過串行連接控制測量設備。有時設備會提供連續的讀數(顯示在屏幕上),有時應用程序需要停止連續測量並進入命令響應模式。C# - 如何切換從串口讀取哪個線程?
如何不做
對於連續測量,XXX使用System.Timers.Timer
爲串行輸入的後臺處理。當定時器觸發時,C#使用其池中的某個線程運行定時器的ElapsedEventHandler
。 XXX的事件處理程序使用具有幾秒超時的阻止commPort.ReadLine()
,然後在有用測量到達串行端口時回撥給代理。這部分工作正常,但...
當其停止實時測量和命令設備做不同的事情時,應用程序試圖通過設置計時器的Enabled = false
掛起GUI線程的後臺處理。當然,這只是設置一個防止進一步事件的標誌,並且已經在等待串行輸入的後臺線程繼續等待。然後GUI線程向設備發送命令,並嘗試讀取回復 - 但回覆由後臺線程接收。現在背景線程變得混亂,因爲它不是預期的測量。 GUI線程同時變得困惑,因爲它沒有收到預期的命令回覆。現在我們知道XXX爲什麼如此片面。
可能的方法1
在另一個類似的應用程序,我用了一個System.ComponentModel.BackgroundWorker
線程自由運行測量。暫停後臺處理我確實在GUI線程兩兩件事:
- 呼叫
CancelAsync
方法的線程上,並且 - 呼叫
commPort.DiscardInBuffer()
,這會導致一個掛起(阻塞,等待)COMPORT讀取在後臺線程拋出一個System.IO.IOException "The I/O operation has been aborted because of either a thread exit or an application request.\r\n"
。
在後臺線程中,我發現這個異常並及時清理,並且所有工作都按預期進行。不幸的是DiscardInBuffer
在另一個線程的阻塞讀取中引發異常沒有記錄的行爲,我可以找到任何地方,我討厭依賴無證行爲。它的工作原理是因爲內部DiscardInBuffer
調用Win32 API PurgeComm,它會中斷阻止讀取(已記錄的行爲)。
可能方法2
直接使用BaseClass Stream.ReadAsync
方法,有監視器取消標記,使用中斷背景IO的支持方式。
由於要接收的字符數是可變的(以換行符結尾),並且框架中不存在ReadAsyncLine
方法,所以我不知道這是否可行。我可以單獨處理每個字符,但性能會受到影響(可能無法在慢速機器上運行,除非線程終止位已在框架內的C#中實現)。
可能的方法3
創建鎖定「我有串行端口」。沒有人從端口讀取,寫入或丟棄輸入,除非他們擁有鎖(包括在後臺線程中重複阻塞讀取)。將後臺線程中的超時值切換爲1/4秒以獲得可接受的GUI響應,而無需太多開銷。
問題
有沒有人有經過驗證的解決方案來處理這個問題? 如何幹淨地停止串口的後臺處理? 我用Google搜索了幾十篇文章,感嘆C#SerialPort
類,但還沒有找到一個好的解決方案。
在此先感謝!
你是不是專注於真正的問題,它是System.Timers.Timer。擺脫它,並使用同步計時器來代替。 –
對不起漢斯,我沒有關注。沒有可能的方法1-3使用System.Timers.Timer;你在暗示什麼? –