如果你有一個掛起操作,如如何殺死一個掛起APM操作
stream.BeginRead(_buffer, 0, _buffer.Length, _asyncCallbackRead, this);
,並關閉該流供應商,如
serialPort.Close();
你意料之中導致異常。
在關閉端口之前,有人可能會取消未決的APM操作嗎?
科爾比的回答並不是我所希望的答案,但他至少確實關閉了一個沒有結果的調查渠道。
很高興我找到了解決方案。
對於每個流,我維護一個類DeviceSession
中的各種狀態信息。該類有一個方法ReadStream
,提供處理傳入數據的AsyncCallback
的實現。
請注意,_asyncCallbackRead
和以下劃線開頭的每個其他變量都是在DeviceSession的構造函數中分配的類私有成員。
構造函數還提供了對_stream.BeginRead
的初始調用。
void ReadStream(IAsyncResult ar)
{
if (IsOpen)
try
{
DevicePacket packet;
int cbRead = _stream.EndRead(ar);
_endOfValidData += cbRead;
while ((packet = GetPacket()) != null)
CommandStrategy.Process(this, packet);
_stream.BeginRead(_buffer, _endOfValidData,
_buffer.Length - _endOfValidData,
_asyncCallbackRead, null);
}
catch (Exception ex)
{
Trace.TraceError("{0}\r\n{1}", ex.Message, ex.StackTrace);
_restart(_streamProvider, _deviceId);
}
}
請注意,我並沒有打算設置ar.AsyncState
。因爲回調委託引用了DeviceSession的特定實例的方法,所以在範圍內,詳細和強類型的上下文信息(包含在此實例的DeviceSession的成員中)自動爲。這是擁有會話對象的要點。
返回關於中止偵聽器的主題,關閉流提供程序會觸發回調,但會嘗試調用IOException
中的EndRead結果。
通常,這樣的異常指示需要偵聽器重新啓動的故障,並且會想要通過重新啓動流提供程序並重新創建會話來作出響應。由於缺乏可靠的與流提供商無關的方式來確定提供商是否出現故障或用戶正在嘗試重新啓動連接(例如,將新設備插入端口),這很複雜。
關鍵是要更多的內容(IsOpen
)添加到DeviceSession
指示所述會話是打開還是已關閉,並用它來順利的完成的ReadStream
最終流產執行。
如果IsOpen
是true
那麼IOException
代表需要恢復的故障。如果IsOpen
是false
故障是故意引起的,不需要採取任何措施。