上位機需循環發送指令並接收回應,但使用 Thread.Sleep 封鎖 UI 執行緒導致 DataReceived 事件無法觸發,造成「發送完畢後才開始接收」的假象。
Thread.Sleep 阻塞了處理訊息迴圈的執行緒(通常是 UI 執行緒),導致 Control.Invoke 無法執行,DataReceived 事件排隊等待。DataReceived 事件中解析完整封包,觸發自訂事件或回呼。TaskCompletionSource 管理「發送-等待回應」的邏輯流程。public partial class MainForm : Form {
private SerialPort _port;
private readonly Queue _sendQueue = new Queue();
private byte[] _expectedResponseHeader; // 用於識別回應封包
private TaskCompletionSource _responseTcs;
public MainForm() {
InitializeComponent();
_port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One);
_port.DataReceived += OnDataReceived;
_port.Open();
}
// 非同步發送並等待回應
public async Task SendCommandAsync(byte[] cmd, byte[] expectedHeader, int timeoutMs = 3000) {
lock (_sendQueue) {
_expectedResponseHeader = expectedHeader;
_responseTcs = new TaskCompletionSource();
_port.Write(cmd, 0, cmd.Length);
}
using var cts = new CancellationTokenSource(timeoutMs);
try {
return await _responseTcs.Task.WaitAsync(cts.Token);
} catch (OperationCanceledException) {
throw new TimeoutException("串口回應逾時");
}
}
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) {
int bytes = _port.BytesToRead;
byte[] buffer = new byte[bytes];
_port.Read(buffer, 0, bytes);
// 簡單封包判斷:檢查Header
if (_responseTcs != null && buffer.Length >= _expectedResponseHeader.Length) {
bool match = true;
for (int i = 0; i < _expectedResponseHeader.Length; i++) {
if (buffer[i] != _expectedResponseHeader[i]) { match = false; break; }
}
if (match) {
_responseTcs.TrySetResult(buffer);
_responseTcs = null;
}
}
// 若需處理黏包/分包,請實作緩衝區累積與封包解析邏輯
}
// 使用範例:按鈕點擊發送指令
private async void btnTest_Click(object sender, EventArgs e) {
try {
byte[] cmd = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x01 }; // Modbus 範例
byte[] header = { 0x01, 0x03 };
byte[] resp = await SendCommandAsync(cmd, header);
txtLog.AppendText("收到: " + BitConverter.ToString(resp) + Environment.NewLine);
} catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
}
async/await 非同步等待,UI 保持回應。SendCommandAsync 返回 Task,呼叫端程式碼線性易讀。CancellationTokenSource 取代迴圈計數,語意清晰。lock 保護;DataReceived 於背景執行緒觸發,UI 更新需 Invoke(範例中 txtLog.AppendText 需自行 Marshal)。System.IO.Pipelines (Core 2.1+) 簡化高效能緩衝處理。採用非同步事件驅動模式,能徹底解決串口通訊中的阻塞、死鎖與 UI 凍結問題,是專業上位機開發的標準做法。