Serial Port 系列(9) 基本篇 -- The Dark Side of the DataReceived Event

DataReceived事件的黑暗面源自於它自己的特性 -- 在次要執行緒上引發 DataReceived 事件,如果你曾經寫過通訊程式會發現一個現象,即使發送端只呼叫一次發送方法,接收端卻有可能會分很多次接收,問題的根源在於傳輸是需要時間的,所以有可能在接收緩衝區還沒收到所有資料前就讀取緩衝區,一個簡單的方式是你可以使用這系列文之前簡單的發送端 與接收端,然後去觀察你每次所收到的資料。

       這個標題是因為我還滿喜歡一個樂團『Pink Floyd』的其中一張專輯『The Dark Side of the Moon』,所以特意模仿,藉以表達對他們的崇敬之意。

 

       DataReceived事件的黑暗面源自於它自己的特性 -- 在次要執行緒上引發 DataReceived 事件,如果你曾經寫過通訊程式會發現一個現象,即使發送端只呼叫一次發送方法,接收端卻有可能會分很多次接收,問題的根源在於傳輸是需要時間的,所以有可能在接收緩衝區還沒收到所有資料前就讀取緩衝區,一個簡單的方式是你可以使用這系列文之前簡單的發送端 與接收端,然後去觀察你每次所收到的資料。

 

       這代表的第一件事情是有可能會在極短的時間內引發多次事件,雖然在MSDN文件庫 [SerialPort.DataReceived 事件]中的備註有提到『PinChanged、DataReceived 和 ErrorReceived 事件可能不會按順序呼叫,而且在基礎資料流報告錯誤和執行事件處理常式之間可能有些許延遲。 一次僅可執行一個事件處理常式。』,我也驗證過確實是如此,正常的情況下它的確會讓前一個事件委派函式結束後再執行DataReceived 事件委派函式。我改寫了前一篇的程式簡單的驗證了這個現象。

 

	Private Sub comport_DataReceived(sender As Object, e As SerialDataReceivedEventArgs)
		Dim buffer(1023) As Byte
		Dim length As Int32 = DirectCast(sender, SerialPort).Read(buffer, 0, buffer.Length)
		Array.Resize(buffer, length)
		MessageBox.Show(BitConverter.ToString(buffer))
	End Sub

 

 

		private void comport_DataReceived(Object sender, SerialDataReceivedEventArgs e)
		{
			Byte[] buffer = new Byte[1024];
			Int32 length = (sender as SerialPort).Read(buffer, 0, buffer.Length);
			Array.Resize(ref buffer, length);
			MessageBox.Show(BitConverter.ToString(buffer));			
		}

       的確,你必須把前一個Messagebox關閉後才會出現下一個Messagebox,這代表它『應該』不會重複引發;但這世上很多事情都不盡如人意,我在測試過程中發現一個很有趣的狀況是在某些時候會造成DataReceived事件委派函式在執行到 SerialPort.Read時緩衝區居然是空的(應該有人眼尖的看出來,我之前的程式並沒有檢查BytesToRead,就是為了突顯這問題),而在沒有設定ReadTimeOut的狀態照常理它應該會被封鎖在這個位置直到有資料可讀為止;不過我在用資源監視器觀察到一旦發生這個現象執行緒與認可記憶體就會開始暴衝,如下圖所示:

DataThread

 

       而且很有趣的,我曾經去計算進入DataReceived事件委派函式與進入後執行完SerialPort.Read的次數,發現只要有一次進入DataReceived事件沒有讀到資料 --比方進入DataReceived事件的次數是215:執行完SerialPort.Read的次數是214,執行緒和認可記憶體就會開始狂衝,套句謝安真的名言『而且它回不去了』,但如果一直不管它會不會發生啥事我就沒有耐心測下去了,因為也許要測很久很久。

 

       是的,用DataReceived事件你就有可能要面對這些奇怪的現象,幸運的是這並非不可以規避,後面的幾篇會慢慢談到正確接收資料的簡單技巧。

 

      在我這段測試黑暗面的過程中感謝忠成、璉璉與bauann幾位朋友給予的協助,因為一度測到快要把頭髮拔光了,幸虧有你們提供的建議讓我可以用更短的時間釐清整個問題。