2012-04-18 69 views
11

私は、受信したSMSメッセージのリストを得るためにGSMモデムと定期的に通信する.net 4.0コンソールアプリケーションを作成しました(これはUSBモデムですが、コードはシリアルポートドライバはATコマンドを送信します - それはSierraワイヤレスモデムですが、私はそれを変更することはできませんし、最新のドライバがあります)。何が起こるかは、ある時間の後(多分数時間、多分数日)、それはちょうど動作を停止します。ここでは、ログのスニペットは、ログの最後であること...SerialPortクラスが時折ハングアップするDispose

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' 

である - 私はここに、少なくとももう1つの文を見ることを期待するよりもかかわらず、何も関連するコードはありません:

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時間ごとにそれを閉じることでおそらく逃げることができます私の質問は...

    このコードには、このbevahiorの原因となっている可能性のある別の問題がありますか、上記で概説した回避策がありますか?

  • +0

    ? – PeskyGnat

    +0

    回避策を使用してみましたか?私はすべての例がWinformsだと理解していますが、[ヒント記事](http://blogs.msdn.com/b/bclteam/archive/2006/10/10/top-5-serialport-tips-_5b00_kim-hamilton_5d00_。 aspx)は問題を非常にはっきりと記述しています。それは少なくともそれを与える価値があるだろう。 –

    +0

    これは単にコマンドを送信して応答を返すだけです(この場合はAT + CPMS = "ME"を送信しました)。これは、「Disposeing the SerialPort for」ログメッセージが表示されているので、実際にそこで何をしたのかはそれほど関連性がありませんでしたか?私がハングにつながることができる何かがありますか? – kmp

    答えて

    12

    SerialPortは多少デッドロックしがちです。あなたが見つけた最も一般的な原因は、DataReceivedイベントハンドラでInvoke()を使用することです。明らかにあなたの場合はここではありません。

    これらのデッドロックは、SerialPortがカーテンの背後で開始するワーカースレッドに関連しています。そのスレッドは、ポート上の非同期イベントを検出するのに役立ちます。ネイティブwinapiはWaitCommEvent()です。その作業者は、DataReceivedイベント、PinChangedイベント、およびErrorReceivedイベントを動作させます。ご注意くださいを行う ErrorReceivedを使用してください。

    Dispose()メソッドはClose()メソッドと同じことを行い、ワーカースレッドが終了するように通知します。しかし、この欠陥はがスレッドを終了するためにを待たないということです。それは問題のレシピです。具体的には、備考セクションのSerialPort.Close()のMSDN記事に記載されています。

    アプリケーションのベストプラクティスは、 Openメソッドを呼び出す前にメソッドを閉じます。ポートは即時に閉じられない可能性があります。

    「ベストプラクティス」アドバイスの最悪の可能性のある練習は、まさにあなたがどれくらい待っているかを正確に指定していないためです。正当な理由から、保証された安全値はありません。 1秒か2秒待つと99.9%が良いはずです。 0.1%の失敗モードは、マシンが頻繁にロードされ、ワー​​カースレッドが単に閉状態を検出するのに十分なサイクルが得られないときに発生します。もちろん、完全にundebuggable。

    この問題を回避するには、プログラムの開始時にシリアルポートを開いて、終了時に閉じるようにしてください。スレッドの問題から短く、これにより、別のプログラムがジャンプしてポートを奪ったときに、ポートへのアクセスがランダムに失われないようにします。また、ポートを閉じることはもはや実際には必要ではないことに注意してください。そうしないと、Windowsが処理します。

    +0

    ありがとう、良い情報 - 私はオプション3に行き、何が起こるか見てみよう!ちなみに、私はちょうどErrorReceivedイベントを気にしなかったなら(このバックグラウンドスレッドはまったく開始されないでしょうか?)そして、それは、オープンではなく、ディスポージに掛かっているので、遅延の事は当てはまりませんでしたか? – kmp

    1

    シリアルポートオブジェクトのDataRecievedイベントまたは他のイベントを使用する場合は、シリアルポートを廃棄する前にイベントハンドラを削除する必要があります。

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

    廃棄されたオブジェクトにイベントが発生したためハングします...これは明らかにバグです。

    しかし、あなたの場合は、これを行いました..ポートが閉じていないため、ハングが発生しています。スレッドを再オープンしようとする前に、thread.sleepによってポートが「解決」される可能性があります。おそらくハードウェアに特有のものかもしれません。それがベストプラクティスがない理由です。

    は、フォームコントロールの場合と同じ: (..)_command.Execute内部で何が起こっているHow to remove all event handlers from a control

    関連する問題