串口SerialPort 死鎖問題

轉載

微軟SerialPort祕籍,SerialPort爲什麼死鎖程序的分析 作者:天涯 來源:中國自學編程網 發佈日期:1232178634

      既然是祕籍,顯然是寫一些大家不常找到的,MSDN裏遺漏提示大家注意的東西。 用過.net 2.0中,自帶SerialPort的人,大多都遇到過。莫名其妙的執行Close的時候會死掉的問題。而Wince,mobile下,甚至Write,WriteLine的時候也會死鎖。這和串口底層驅動關。    

1.Close的時候死機問題 我只是猜測,Close執行的操作是調用2個API:

 SetCommMask(m_hComPort,0);

WaitForSingleObject(m_pThread->m_hThread,INFINITE);

 m_pThread=NULL;

i(m_hComPort=INVALID_HANDLE_VALUE) CloseHandle(m_hComPort);

 這裏的話,SetCommMask操作的時候。如果你的DeviceReceived事件沒執行完,裏面還有更新界面的操作。就會監聽線程在等待,執行到界面操作,界面又要求關閉監聽線程然後關閉串口。死鎖了。 [解決辦法] 一個正確不會死機的串口程序(主要是DataReceived事件裏如果要調用ui來顯示數據或分析結果的時候。ui的效率不確定,ui可能快。可能慢,可能用戶某個操作阻塞中,會導致DataReceived事件執行很長時間,不論如何,這個時間我們無法預料)。 應該在關閉之前執行這句

while (Environment.TickCount - i < 2000 && _isReceiving)

Application.DoEvents();

 2.WriteLine,Write死鎖 串口必然會讀寫超時,你無法預測你軟件運行的硬件都足夠快,串口驅動都效率很高。並且讀寫超時是正常現象。但是微軟的SerialPort類默認的讀寫超時都是-1,無限等待,如果串口驅動有效率陷阱,超時了。Write和WriteLine就導致軟件死鎖無法繼續使用了。 [解決辦法] 設置讀寫超時爲1000毫秒 3.不知道有人注意過沒,WriteLine好像和Write效果一樣?不是一個錯誤,而是微軟一個很奇怪的設計,他允許設置換行符,默認是"",你需要自己設置NewLine屬性爲"/r/n"。否則Write和WriteLine是一樣的。 -------------------------------------------------------------------------------- 下面演示一個標準的,不會軟件死鎖的基於SerialPort的例子:

 using System;

 using System.Drawing;

using System.Text;

 using System.Windows.Forms;

using System.IO.Ports;

using System.Collections.Generic;

namespace SerialSample

{

 public partial class FormSerialSample : Form

{

 //Fields SerialPort _comm = new SerialPort("COM1", 19200);

 bool _isReceiving = false; //wince下4096字節爲一頁,一次分配一頁內存可能會快。但不知道託管內存是否也如此

 List buf = new List(4096);

 //Construct public FormSerialSample() { InitializeComponent();

 }

bool Open()

{

_comm.WriteTimeout = 1000;//寫超時,如果底層串口驅動效率問題,能有效的避免死鎖

_comm.ReadTimeout = 1000;//讀超時,同上

_comm.NewLine = "/r/n";//新行的文本,用於WriteLine方法中由系統附加在text後

_comm.DataReceived += OnComm;//註冊事件

_comm.Open();//打開串口

return _comm.IsOpen;//返回打開結果

}

void Close()

{

//這裏需要允許OnComm方法執行完。如果不這麼做。可能會

//先由某個地方執行到了Close,然後監聽線程觸發,調用了OnComm

 //OnComm執行完緩存,執行this.Invoke。不知道Invoke原理。可能是

 //消息機制?那麼你如果正在這個函數裏,不釋放的時候,你就不會繼續

//處理消息,所以我們要關閉之前,手工的調用消息處理函數

_comm.DataReceived -= OnComm;//反註冊事件,避免下次再執行進來。

//最大延遲2秒,並檢測到OnComm退出則退出,處理系統消息隊列中的消息

int i = Environment.TickCount;

while (Environment.TickCount - i < 2000 && _isReceiving) Application.DoEvents();

_comm.Close();//現在沒有死鎖了,關閉串口

}

void OnComm(object sender, SerialDataReceivedEventArgs e)

 {

 _isReceiving = true;//開始讀 //緩存你的數據,注意,最好在Invoke之外緩存數據,只有更新UI的才需要

Invoke int count = _comm.BytesToRead;

if (count < 1)

return;

 byte[] tmpBuf = new byte[count];

 _comm.Read(tmpBuf, 0, count); //連續3k數據沒分析,應該沒啥用了。

//避免數據太多分析時候時間長導致效率明顯下降

if (buf.Count > 3000)

buf.Clear();

 buf.AddRange(tmpBuf); //分析數據

//自己實現

//匿名委託,用於this.Invoke調用。

EventHandler delUpdate = delegate

{

//執行你的更新ui操作

};

this.Invoke(delUpdate); _isReceiving = false;

//結束讀

}

 }

 }

串口是沒啥技術含量。但是這個微軟的SerialPort實在給不少人帶來了麻煩,死機是絕大多數人遇到的,我以前是MSDN,微軟的技術論壇,CSDN,GOOGLE,Baidu都搜索了。甚至發了郵件給微軟的wince開發組,沒有任何答案。不求您覺得我寫的有什麼價值,但希望對有同樣困惑的朋友。能搜索到此文章時候,解決一個隱含的問題

發佈了23 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章