2016-12-01 119 views
1

对不起,但我很困惑。我们有一个将命令发送到一个硬件的进程,它会被淹没。我创建了一个简单的解决方案,每完成100次发送,它就会暂停1秒,然后继续处理。以调试模式运行,这完全解决了我们遇到的所有问题。但是,当我将这个解决方案编译成Release版本时,我的计时器方法似乎永远停滞不前。发布版本的运行方式与调试版本不同

在下面的代码中,我有一个简单的while循环,循环直到bool为true。 (我不想使用睡眠,因为我不想让线程变得反应迟钝做)

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

make'isWaitOver' volitile。 https://msdn.microsoft.com/en-us/library/x13ttww7.aspx – flakes

+0

您不显示声明,但为了使此代码可靠,isWaitOver必须是易失性的。是吗? –

+0

Thread.Sleep是作为一个循环 – NtFreX

回答

1

您的代码似乎在其中存在争用条件。启动计时器首先启用计时器,然后将isWaitOver设置为false。当它运行时,然后将isWaitOver设置为true。这是不太可能的,但是在繁忙的系统中,定时器可能在主线程设置isWaitOverfalse之前触发OnTimedEvent。如果发生这种情况,那么isWaitOver可能总是以虚假形式出现在您的循环中。为防止这种情况,请在aTimer.Enabled = true之前将您的isWaitOver = false行删除。

更可能出现的问题是使用优化程序重新排序代码中的内容。如果单个线程不会注意到这种差异,但它可以在这样的多线程场景中引发问题,则可以这样做。要解决此问题,您可以在代码中制作isWaitOvervolatile或将memory barriers。请参阅Threading in C# by Joseph Albahari以获得良好的撰写。

通常,当它达到易失性和内存障碍有所改善的地步时,您已经使自己的代码变得复杂而脆弱。内存障碍是非常先进的事情,非常容易出错,几乎不可能正确测试(例如,行为因所用的CPU型号而异)。我的建议是将isWaitOver切换为ManualResetEvent并等待它通过计时器线程发送信号。这还有一个好处,就是可以防止你的代码进入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

你修好了!非常棒!感谢您的详细解释。如果我再次需要这样的解决方案,我会为未来保留这个解决方案。 –

相关问题