在使用DataReceived事件的時候要避免執行緒暴衝的狀況有幾個簡單的方式可以避免。
在使用DataReceived事件的時候要避免執行緒暴衝的狀況有幾個簡單的方式可以避免。
在讀取前檢查 BytesToRead屬性值
SerialPort類別有一個 [SerialPort.BytesToRead 屬性] 可以檢查目前在讀取緩衝區內的位元組數,所以我們可以利用此屬性先檢查緩衝區是否有資料可供讀取,如果有的話才呼叫 Read 方法,藉此避免在緩衝區被清空的狀態下執行Read。我們可以把之前在 [Serial Port 系列(8) 基本篇 -- 使用DataReceived 事件接收資料] 中的 comport_DataReceived 方法內容改成以下的程式碼。
Private Sub comport_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
Dim buffer(1023) As Byte
If DirectCast(sender, SerialPort).BytesToRead > 0 Then
Dim length As Int32 = DirectCast(sender, SerialPort).Read(buffer, 0, buffer.Length)
Array.Resize(buffer, length)
Dim d As New Display(AddressOf DisplayText)
Me.Invoke(d, New Object() {buffer})
End If
End Sub
private void comport_DataReceived(Object sender, SerialDataReceivedEventArgs e)
{
if ((sender as SerialPort).BytesToRead > 0)
{
Byte[] buffer = new Byte[1024];
Int32 length = (sender as SerialPort).Read(buffer, 0, buffer.Length);
Array.Resize(ref buffer, length);
Display d = new Display(DisplayText);
this.Invoke(d, new Object[] { buffer });
}
}
設定ReadTimeout屬性
設定[SerialPort.ReadTimeout 屬性] 也是一個可以使用的方式,在沒有設定此屬性的情況,當程式執行到 SerialPort.Read時執行緒會封鎖在這個位置直到讀取資料完成為止;一旦有指定非InfiniteTimeout的數值時,在指定時間內沒有讀取到任何資料就會發生[TimeoutException] ,因此就可以在程式中攔截此例外狀況並加以處理。雖然我在測試BytesToRead的過程中並未發現有例外產生的現象,但我也不敢百分之百的確定萬無一失,所以這個方式等於是再加一層保險。
所以這要修改兩個地方,一是在建立執行個體後設定屬性
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
comport = New SerialPort("COM5", 9600, Parity.None, 8, StopBits.One)
comport.ReadTimeout = 2000
AddHandler comport.DataReceived, AddressOf comport_DataReceived
If comport.IsOpen = False Then
comport.Open()
End If
End Sub
private void Form1_Load(object sender, EventArgs e)
{
comport = new SerialPort("COM5", 9600, Parity.None, 8, StopBits.One);
comport.ReadTimeout = 2000;
comport.DataReceived += new SerialDataReceivedEventHandler(comport_DataReceived);
if (!comport.IsOpen)
{
comport.Open();
}
}
第二則是在DataReceived Event委派函式中攔截Timeout
Private Sub comport_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
Dim buffer(1023) As Byte
If DirectCast(sender, SerialPort).BytesToRead > 0 Then
Try
Dim length As Int32 = DirectCast(sender, SerialPort).Read(buffer, 0, buffer.Length)
Array.Resize(buffer, length)
Dim d As New Display(AddressOf DisplayText)
Me.Invoke(d, New Object() {buffer})
Catch timeoutEx As TimeoutException
'以下這邊請自行撰寫你想要的例外處理
Catch ex As Exception
'這邊請自行撰寫你想要的例外處理
End Try
End If
End Sub
private void comport_DataReceived(Object sender, SerialDataReceivedEventArgs e)
{
if ((sender as SerialPort).BytesToRead > 0)
{
try
{
Byte[] buffer = new Byte[1024];
Int32 length = (sender as SerialPort).Read(buffer, 0, buffer.Length);
Array.Resize(ref buffer, length);
Display d = new Display(DisplayText);
this.Invoke(d, new Object[] { buffer });
}
catch (TimeoutException timeoutEx)
{
//以下這邊請自行撰寫你想要的例外處理
}
catch (Exception ex)
{
//以下這邊請自行撰寫你想要的例外處理
}
}
}
這兩個方式我大概測試了幾次是沒發生什麼問題,如果有使用這方式還產生執行緒暴衝的情形還請回覆讓我知道一下。