2016-12-01 4 views
1

申し訳ありませんが、私は困惑しています。私たちにはハードウェアにコマンドを送信するプロセスがあり、それは圧倒されます。私は、100回の送信ごとに、処理を続ける前に1秒間休止する簡単な解決策を作成しました。デバッグモードで実行すると、これは私たちが経験していたすべての問題を完全に解決しました。しかし、私はこのソリューションをリリースビルドにコンパイルすると、私のタイマーメソッドは一見永遠に回転を停止するようになります。リリースビルドはデバッグビルドとは異なる動作をします

次のコードでは、シンプルなwhileループを使用していますが、これはブールが真になるまでループします。 (私は、スレッドが応答しなくなってきてほしくないので、私は、睡眠を使いたくありませんでした)

foreach (DataRow row in ds.Tables[0].Rows) 
{ 
    string Badge = Database.GetString(row, "Badge"); 
    if (Badge.Length > 0) 
    { 
     if(Count < Controller.MaximumBadges) 
     { 
      if (processed == 100) // Every 100 downloads, pause for a second 
      { 
       processed = 0; 
       StartTimer(); 
       while (!isWaitOver) 
       { 
       } 
       Controller.PostRecordsDownloadedOf("Badges", Count); 
      } 

      if (Download(Badge, false)) 
      { 
       Count++; 
       processed++; 
      } 
     } 
     else 
      Discarded++; 
    } 
    TotalCount++; 
} 

private void StartTimer() 
{ 
    // Create a timer with a one second interval. 
    aTimer = new System.Timers.Timer(1000); 
    // Hook up the Elapsed event for the timer. 
    aTimer.Elapsed += OnTimedEvent; 
    aTimer.AutoReset = true; 
    aTimer.Enabled = true; 
    isWaitOver = false; 
} 

private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) 
{ 
    isWaitOver = true; 
    aTimer.Enabled = false; 
} 

誰もがリリースモードで実行しているときにwhileループが無限に貼り付けてしまう理由を見ることができますか?また、誰かがこれに対するより良い解決策を見たら、私に教えてください。私はVS 2010を使用する必要があります。

読んでいただきありがとうございます。

+1

「isWaitOver」を作成します。 https://msdn.microsoft.com/en-us/library/x13ttww7.aspx – flakes

+0

宣言は表示されませんが、このコードが信頼できるものであるためには、isWaitOverはvolatileでなければなりません。それは...ですか? –

+0

Thread.Sleepはループとしてです – NtFreX

答えて

1

コードに競合状態があるようです。タイマーを開始するには、タイマーを有効にしてからisWaitOverfalseに設定します。 OnTimedEventを実行すると、isWaitOvertrueに設定されます。少しはそうですが、忙しいシステムでは、メインスレッドがisWaitOverからfalseに設定する前に、タイマーがOnTimedEventを発動する可能性があります。これが発生すると、isWaitOverはループに常にfalseと表示されることがあります。これを防ぐにはaTimer.Enabled = trueの前にisWaitOver = false行を入れてください。

問題の可能性が高いのは、オプティマイザがコードの内容を並べ替えることです。これは、単一のスレッドが違いに気づかないが、このようなマルチスレッドのシナリオで問題を引き起こす可能性がある場合にこれを行うことができます。これを解決するにはisWaitOvervolatileを入力するか、コードにmemory barriersを入力します。良い書込みについては、Threading in C# by Joseph Albahariを参照してください。

一般的に、揮発性とメモリの壁が差をつけているところに行くと、コードは複雑で壊れやすいものになりました。メモリバリアは非常に高度なものであり、間違って正しくテストすることはほとんど不可能です(たとえば、使用しているCPUモデルによって動作が異なります)。私の助言はisWaitOverManualResetEventに切り替え、それがタイマースレッドによって通知されるのを待つことです。これには、コードをCPUホッギングスピンループに入れないという利点があります。

最後に、コードにハンドルリークがあります。新しいTimerオブジェクトを作成するたびに、それをもう一度処分することはありません。私が示したように新しいものを作成する前にそれを破棄するか、単純に再作成して再作成しないでください。

ManualResetEvent isWaitOver = new ManualResetEvent(false); 

    private void Run() 
    { 
     foreach (DataRow row in ds.Tables[0].Rows) 
     { 
      string Badge = Database.GetString(row, "Badge"); 
      if (Badge.Length > 0) 
      { 
       if (Count < Controller.MaximumBadges) 
       { 
        if (processed == 100) // Every 100 downloads, pause for a second 
        { 
         processed = 0; 
         StartTimer(); 
         isWaitOver.WaitOne(); 
         Controller.PostRecordsDownloadedOf("Badges", Count); 
        } 

        if (Download(Badge, false)) 
        { 
         Count++; 
         processed++; 
        } 
       } 
       else 
        Discarded++; 
      } 
      TotalCount++; 
     } 
    } 

    private void StartTimer() 
    { 
     // Create a timer with a one second interval. 
     if (aTimer != null) aTimer.Dispose(); 
     aTimer = new System.Timers.Timer(1000); 
     // Hook up the Elapsed event for the timer. 
     isWaitOver.Reset(); 
     aTimer.Elapsed += OnTimedEvent; 
     aTimer.AutoReset = true; 
     aTimer.Enabled = true; 
    } 

    private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e) 
    { 
     aTimer.Enabled = false; 
     isWaitOver.Set(); 
    } 
+0

あなたはそれを修正しました!全く素晴らしい!詳しい説明もありがとう。もし私がこのような修正をもう一度必要とするならば、私はこの解決策を将来のために留めておきます。 –

関連する問題