[英].NET SerialPort DataReceived event thread interference with main thread
我正在使用 C# 中的 SerialPort 類編寫串行通信程序,以與通過 RS232 電纜連接的脫衣機進行交互。 當我將命令發送到機器時,它會根據命令以一些字節進行響應。 就像我發送“\D”命令時,我希望下載 180 字節的機器程序數據作為一個連續的字符串。 根據機器手冊,建議發送一個無法識別的字符(如逗號 (,) 字符)作為最佳做法,以確保在發送循環中的第一個命令之前機器已初始化。 我的串口通信代碼如下:
public class SerialHelper
{
SerialPort commPort = null;
string currentReceived = string.Empty;
string receivedStr = string.Empty;
private bool CommInitialized()
{
try
{
commPort = new SerialPort();
commPort.PortName = "COM1";
if (!commPort.IsOpen)
commPort.Open();
commPort.BaudRate = 9600;
commPort.Parity = System.IO.Ports.Parity.None;
commPort.StopBits = StopBits.One;
commPort.DataBits = 8;
commPort.RtsEnable = true;
commPort.DtrEnable = true;
commPort.DataReceived += new SerialDataReceivedEventHandler(commPort_DataReceived);
return true;
}
catch (Exception ex)
{
return false;
}
}
void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort currentPort = (SerialPort)sender;
currentReceived = currentPort.ReadExisting();
receivedStr += currentReceived;
}
internal int CommIO(string outString, int outLen, ref string inBuffer, int inLen)
{
receivedStr = string.Empty;
inBuffer = string.Empty;
if (CommInitialized())
{
commPort.Write(outString);
}
System.Threading.Thread.Sleep(1500);
int i = 0;
while ((receivedStr.Length < inLen) && i < 10)
{
System.Threading.Thread.Sleep(500);
i += 1;
}
if (!string.IsNullOrEmpty(receivedStr))
{
inBuffer = receivedStr;
}
commPort.Close();
return inBuffer.Length;
}
}
我從 Windows 窗體調用此代碼,如下所示:
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
If(inBuffer == "!?*O")
{
len = SerialHelperObj.CommIO("\D",2,ref inBuffer, 180)
}
串行端口的有效返回值如下所示:\D00000010000000000010 550 3250 0000256000 等等...
我得到這樣的東西:\D00000010D,, 000 550 D,,等等...
當我發送命令時,我覺得我的通訊呼叫受到了干擾。 但我試圖確保逗號命令的結果然后啟動實際命令。 但是接收線程正在插入來自前一個通信周期的字節。
任何人都可以闡明這一點......? 為了完成這項工作,我掉了很多頭發。 我不確定我哪里做錯了
串行端口不是線程安全資源,使用它們的包裝器或您用來保存結果的字符串也不是。 發生的情況是 DataReceived 事件處理程序被多次觸發,並且您最終會同時執行多個處理程序。 嘗試向您的事件處理程序添加一個鎖塊:
...
Object lockingObj = new Object();
...
void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock(lockingObj)
{
SerialPort currentPort = (SerialPort)sender;
currentReceived = currentPort.ReadExisting();
receivedStr += currentReceived;
}
}
lock 語句可防止多個線程同時執行塊中的代碼,因此 receivedStr 將在寫入另一個 ReadExisting 之前獲取一個 ReadExisting 的全部內容。
請注意,這並不能保證執行順序; 先進者獲勝。 所以,假設線程 1 先到達這里,然后線程 2 在 250 毫秒后到達,然后線程 3 在 250 毫秒后到達。 線程 1 可能會先進入,執行需要一段時間的讀取,然后退出。 在那段時間,線程 2 和 3 進入函數並被鎖定語句阻塞,等待線程 1 釋放它的鎖。 一旦發生這種情況,就取決於內核首先調度哪個線程,並且可能是線程 3,具體取決於許多操作系統和硬件因素。
為什么你每次都重新初始化你的端口? 我想一個就夠了。
我發現您的代碼存在許多問題。
false
是忽略異常。 你不知道當你這樣做時發生了哪個異常。commPort
對象可能從未被實例化。 它可能處於任何狀態,但您將忽略異常並繼續訪問可能無效的對象。DataReceived
事件可以在端口打開時隨時發生。 它可能會也可能不會在同一個線程上引發。 您的代碼可能正在從事件處理程序下關閉該端口,這會引發您的代碼未捕獲的異常。receivedStr += currentReceived
不會附加到receivedStr
- 它會創建一個新的字符串對象來保存這兩個部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.