在下面的代码中,我有一个简单的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。
感谢阅读。
答案 0 :(得分:1)
您的代码似乎有竞争条件。首先启动计时器启用计时器,然后将isWaitOver
设置为false
。 OnTimedEvent
运行时会将isWaitOver
设置为true
。这有点不太可能,但在繁忙的系统上,计时器可能会在主线程设置OnTimedEvent
到isWaitOver
之前触发false
。如果发生这种情况,那么isWaitOver
可能总是以错误的形式出现在您的循环中。为防止这种情况,请将isWaitOver = false
行放在aTimer.Enabled = true
之前。
更有可能的问题是优化器重新排序代码中的内容。如果单个线程没有注意到差异,则允许这样做,但是这可能会导致像这样的多线程场景中出现问题。要解决此问题,您可以isWaitOver
volatile
或将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();
}