2011-01-25 21 views
6

私のWindowsサービスアプリケーションでは、タイマーをたくさん使用しています。私はSystem.Timersだけを使用しています。 私は前にこの問題を経験したことがありませんが、突然、私はこの例外だ:私はタイマーを停止し、タイマー間隔を変更しています私の方法では.NET 3.5 C#System.TimerでのバグSystem.ObjectDisposedException:廃棄されたオブジェクトにアクセスできない

System.ObjectDisposedException: Cannot access a disposed object. 
    at System.Threading.TimerBase.ChangeTimer(UInt32 dueTime, UInt32 period) 
    at System.Threading.Timer.Change(Int32 dueTime, Int32 period) 
    at System.Timers.Timer.UpdateTimer() 
    at System.Timers.Timer.set_Interval(Double value) 
    at MyApp.MySpace.MySpace2.MyClassWithTimer.MethodChangeTimerInterval() 

を。それが私が例外を得た場所です。

私はこのバグについて何かを読んだことがありますが、.NET 3.5でもこのバグが残っていますか?

どうすれば修正できますか?停止後にタイマーオブジェクトを更新し、その間隔を新しいオブジェクトに設定する必要がありますか? GC.KeepAlive(dataTimer)を使用しています。

編集:私はリンクとすぐにタイマーを停止として基本的に http://www.kbalertz.com/kb_842793.aspx を見つけました*

は、内部 System.Threading.Timerがために利用できるようになります。私はこの問題について、いくつかの他の質問を見つけた ガベージコレクション は、時には経過イベントが発生しないようにするか、時には が廃棄参照例外を引き起こすことがあります。 記事には記載されていませんが、私の解決策は、タイマが になり、経過イベントを再追加するたびに新しいタイマーを作成するために でした。効率的ではありませんが、簡単には、 と私には問題ありません。 これは私の問題を完全に解決しました。

*。答えしかし、私はバグがまだそこにある、と私はタイマーを再追加することは良い考えであることを確認する必要が理由として混乱していますすべての人のための 乾杯...

コードこと

private void StartAsyncResponseTimer() 
{ 
    switch (_lastRequestType) 
    { 
     case 1: 
      asyncResponseTimer.Interval = 1000; 
      break; 
     case 2: 
      asyncResponseTimer.Interval = 2000; 
      break; 
     case 3: 
      asyncResponseTimer.Interval = 3000; 
      break; 
     default: 
      asyncResponseTimer.Interval = 10000; 
      break; 
    } 

    asyncResponseTimer.Start(); 
} 

機能がSerialPortDataReceivedイベントから呼び出されました:

private void SerialPortDataReceived(object sender, EventArgs e) 
{ 
     StartAsyncResponseTimer(); 
} 

タイマーが変化する間隔を呼び出す前に停止したエラーの原因となりました。

private Timer asyncResponseTimer = new Timer(); 

EDIT:

タイマーは、私のクラスのプライベートなフィールドであるアプリケーションは、行の数ヶ月のために実行されている、これは私がこの例外を得た最初の時間です!

マイ処分パターン:

public class SerialPortCommunication{ 

... 

    private void SerialPortDataReceived(object sender, EventArgs e) 
    { 
     ReadResponse(); 

     StartAsyncResponseTimer(); 
    } 

    //used to determine if is recieving response over 
    private void StartAsyncResponseTimer() 
    { 
     switch (_lastRequestType) 
     { 
      case 1: 
       asyncResponseTimer.Interval = 1000; 
       break; 
      case 2: 
       asyncResponseTimer.Interval = 2000; 
       break; 
      case 3: 
       asyncResponseTimer.Interval = 3000; 
       break; 
      default: 
       asyncResponseTimer.Interval = 10000; 
       break; 
     } 

     asyncResponseTimer.Start(); 
    } 

    public virtual void Dispose() 
    { 

     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    private void Dispose(bool disposing) 
    { 
     if (!this._disposed) 
     { 
      if (disposing) 
      { 
       // Dispose managed resources. 

      } 

      // Dispose unmanaged resources. 
      _disposed = true; 

      Stop(); 

     } 
    } 

    ~SomeClass() 
    { 

     Dispose(false); 
    } 

    #endregion 




    public void Stop() 
    { 
     _asyncResponseTimer.Stop(); 
     serialPortManager.ClosePort(); 
    } 
} 
+0

エラーメッセージと同様に、コードを入力してください。私の推測では、あなたがしてはいけないときに終了したら何かを自動的に破棄する「使用する」ブロックを使用していると思います。私たちはあなたのコードを見て、あなたに教えようとします。 – Chris

+2

タイマーを使うときの私のテクニックの価値は、どこかで(例えば私的なフィールドやタイマーの集まり)参照を保持して、参照があることを知ることです。私がしたいと思う前に彼らがGCedされないことを私が知っている方法。もちろん、タイマーオブジェクトの処理が完了したら、その参照を削除する必要があります。 – Chris

+0

私はタイマーをクラスのプライベートメンバーとして参照しています。 – Simon

答えて

1

が、それはあなたがあなたのタイマーを配置されているだけで後シリアルポートデータを取得するということはできますか?あなたが掲示したデータで私の頭に浮かぶのは唯一のことです!あなたはStop()で何をしているのですか? Dispose()内部のメソッド

+0

私はtimer.Dispose()関数を呼び出すことはありません。シリアルポートの通信を停止するにはStop()が必要です。私はStart()メソッドも持っています。私はクラスSerialPortCommunicationを処分するとき、私はまた、別のスレッド上にあるので、廃棄後に発生しないようにタイマーを停止します。その例外は処分されませんでした。それはシリアルポートとの通信の途中で起こる。 SerialPortDataReceived()が発生するたびに、タイマの間隔をリセットします。 – Simon

+0

私の編集plsを参照してください。 – Simon

1

シリアルポートでデータを受信するときにタイマーを起動したようです。タイマーが応答の送信を完了する前にポートで追加のデータを受信するとどうなりますか?あなたが掲示した情報に基づいて、タイマー間隔が変更され、タイマーがまだそのタイマーイベントを処理している間に(再び)開始されるようです。

あなたは上記のシナリオを考えましたか?タイマーの自動リセットかどうかあなたはいつStop()を呼んでいますか?

+0

受信したデータがないかどうかを判断するタイマーがあります。したがって、serialPortDataReceivedが発生するたびにタイマーが再起動されます。タイマーが仕事を終えたら、私はすべてのデータが受信されたことを知り、私は依頼を続けることができます。タイマーはデフォルトのAutoReset = trueを持ちます。私はシリアルポート通信を停止したいときにだけStop()を呼び出しています。その例外が発生したときにStop()は呼び出されませんでした。 UIから基本的にユーザーによって呼び出されます。 – Simon

1

サーバー構成を指定していないので、私はWindows Server 2003を前提としています.2ヶ月稼働していると言えば、49.7 days bugを思い出します。 3月に再びクラッシュしない限り、あなたのシナリオには当てはまらないかもしれません。良いです - あなたはクラスレベルでタイマーを宣言したので:-)

Symptom 2
On a Windows Server 2003-based computer that is not running ISA Server, a similar problem occurs when the following conditions are true: You call the CreateTimerQueueTimer function repeatedly in an application. You set a specified period to trigger the timer that is created by the CreateTimerQueueTimer function.

The application runs for more than 49.7 days. After 49.7 days, the timer is triggered immediately instead of being triggered after the specified period. After several minutes, the timer is triggered correctly.

キープアライブは、()ここでは有用ではありません。間隔を変更するためにタイマーを停止する必要はありません。

既に提供されているコールスタックとコードから、すでに削除されたタイマーの間隔を変更しようとしているようです。進行中の作業を妨げていると思われる廃棄パターンでタイマーを停止するのではなく、タイマーを停止する必要があります。つまり、最初に実行する作業があるかどうかを確認し、そうでない場合は、ポートの受信を停止し、タイマーを停止します。イベントハンドラ、serialPortManager宣言などのコードの一部を見ることができない限り、多くの助けになることはありません。

関連する問題