2012-04-18 55 views
11

我已經寫了一個.net 4.0控制檯應用程序,它週期性地與GSM調制解調器通信,以獲取接收到的SMS消息列表(它是USB調制解調器,但代碼通過串行端口連接到它驅動程序併發送AT命令 - 順便說一下,它是一個Sierra無線調制解調器,但我無法更改它,並且我擁有最新的驅動程序)。會發生什麼是在一段時間(可能是幾小時,也許幾天)之後,它會停止工作。下面是一個日誌片斷......SerialPort類偶爾掛起處置

2012-04-17 23:07:31 DEBUG Modem Check (108) - Executing AT command 'AT+CPMS="ME"'... 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Finished executing 'AT+CPMS="ME"' 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Detaching event handlers for 'COM13' 
2012-04-17 23:07:31 DEBUG Modem Check (108) - Disposing the SerialPort for 'COM13' 

這是日誌的末尾 - 僅此而已儘管我希望看到至少多了一個聲明,這裏是有關的代碼:

internal T Execute() 
{ 
    var modemPort = new SerialPort(); 
    T ret; 

    try 
    { 
     modemPort.ErrorReceived += ModemPortErrorReceived; 

     modemPort.PortName = _descriptor.PortName; 
     modemPort.Handshake = Handshake.None; 
     modemPort.DataBits = 8; 
     modemPort.StopBits = StopBits.One; 
     modemPort.Parity = Parity.None; 
     modemPort.ReadTimeout = ReadTimeout; 
     modemPort.WriteTimeout = WriteTimeout; 
     modemPort.NewLine = "\r\n"; 
     modemPort.BaudRate = _descriptor.Baud; 

     if (!modemPort.IsOpen) 
     { 
      modemPort.Open(); 
     } 

     ret = _command.Execute(modemPort, _logger); 

     _logger.Debug("Detaching event handlers for '{0}'", 
         _descriptor.PortName); 

     modemPort.ErrorReceived -= ModemPortErrorReceived; 

     _logger.Debug("Disposing the SerialPort for '{0}'", 
         _descriptor.PortName); 
    } 
    catch (IOException ex) 
    { 
     _logger.Error(ex.Message); 

     throw new CommandException(
      string.Format(CultureInfo.CurrentCulture, 
          ModemWrapperStrings.COMMAND_ERROR, 
          ex.Message), 
      ex); 
    } 
    catch (UnauthorizedAccessException ex) 
    { 
     _logger.Error(ex.Message); 

     throw new CommandException(
      string.Format(CultureInfo.CurrentCulture, 
          ModemWrapperStrings.COMMAND_ERROR, 
          ex.Message), 
      ex); 
    } 
    finally 
    { 
     modemPort.Dispose(); 

     _logger.Debug("Modem on port '{0}' disposed", 
         _descriptor.PortName); 
    } 

    return ret; 
} 

正如你所看到的,它掛在了SerialPort類的Dispose方法上。

我做了一些谷歌搜索,我來到這個問題:Serial Port Close Hangs the application從這個線程:serial port hangs whilst closing。這種共識似乎是在一個不同的線程中關閉這個端口,但這只是一個表單應用程序?在我的情況下,我有一個簡單的控制檯應用程序,所以我不認爲它適用(它只是在主線程中循環運行)。我甚至不確定這是否是這個問題(我的感覺是,更可能是調制解調器的串口驅動程序存在問題,但我不知道,也許我對調制解調器不公平)。據我看到它,我有三種選擇:

  1. 關閉在不同的線程
  2. 在延遲把端口關閉端口
  3. 前離開端口打開永遠

我並不是真的喜歡這些解決辦法中的任何一種,但我正在考慮將端口打開並只看到會發生什麼(我有這種感覺會泄漏內存或更糟,揭露調制解調器的其他問題,但也許我只是悲觀並且如果是這種情況,我可能會每24小時關閉一次,s唉,並重新打開它)所以我的問題是...

是否有這個代碼可能會導致這個bevahior或有替代解決方法的替代問題,我已經概述上面?

+0

這是怎麼回事裏面_command.Execute(..)? – PeskyGnat 2012-04-18 12:19:45

+0

您是否嘗試使用建議的解決方法?我明白所有的例子都是Winforms,但[提示文章](http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_。 aspx)非常清楚地描述了這個問題。這至少值得一試。 – 2012-04-18 12:23:14

+0

它只是發送命令並獲取響應(在這種情況下,它發送AT + CPMS =「我」) - 它出來的罰款,雖然因爲我得到了「丟棄串口」日誌消息,所以我想它在那裏實際上做了什麼並不太相關?有沒有我能做的事情會導致死機? – kmp 2012-04-18 12:23:16

回答

12

SerialPort有點容易發生死鎖。到目前爲止,最常見的原因是您找到的那個,它是通過在DataReceived事件處理程序中使用Invoke()來觸發的。顯然不是你的情況在這裏。

這些死鎖與SerialPort在幕後啓動的工作線程有關。該線程有助於檢測端口上的異步事件,底層本地winapi是WaitCommEvent()。該工作人員使DataReceived,PinChanged和ErrorReceived事件起作用。注意你如何使用ErrorReceived。

Dispose()方法與Close()方法的作用相同,它指示工作線程退出。然而,該缺陷是它不等待線程退出。這是一個麻煩的祕訣,這種情況明確記錄在備註部分中的SerialPort.Close()MSDN文章中:

任何應用程序的最佳實踐是在調用完成後等待一段時間嘗試調用Open方法之前關閉方法,因爲該端口可能不會立即關閉。

坦率地說,對於「最佳實踐」建議來說,這可能是最糟糕的做法,因爲它沒有完全指定您應該等待多久。有一個很好的理由,沒有保證安全的價值。等待一兩秒鐘應該是99.9%好。當機器負載過重,工作線程沒有獲得足夠的週期以及時檢測到關閉條件時,會發生0.1%的故障模式。當然完全不可剝奪。

解決了這個問題,只有在程序啓動時打開一個串口並在退出時關閉它。由於線程麻煩,這還可以確保在另一個程序跳入時將不會隨機失去對端口的訪問權限,並將端口從您的端口​​中偷走。並且請注意,關閉端口實際上不再必要,如果不這樣,Windows會照顧它。

+0

謝謝,很好的信息 - 我會選擇3然後看看會發生什麼!順便說一句,如果我只是不打擾ErrorReceived事件(這是否意味着後臺線程根本不會啓動)呢?它掛在Dispose上,而不是Open,所以我認爲延遲的東西不適用於它? – kmp 2012-04-18 13:29:29

1

如果您正在使用DataRecieved事件或來自串口對象的任何其他事件,則應在處理串口之前從其中刪除事件處理程序。

mySerial.DataReceived -= DataReceivedHandler; 
mySerial.Dispose(); 

發生掛起是因爲你有一個事件發生在一個已處理的對象上......這顯然是一個錯誤。

但是,在你的情況下,你已經做到了..由於端口尚未關閉而發生掛起。可能是一個thread.sleep可能允許端口在您嘗試重新打開之前「解決」。它可能也是硬件專用的......這就是爲什麼沒有最佳實踐。

一樣的一個窗體控件: How to remove all event handlers from a control