发布构建运行方式不同于调试构建
Release build runs differently than Debug build
抱歉,我很困惑。我们有一个向硬件发送命令的进程,该进程被淹没了。我创建了一个简单的解决方案,在每发送 100 次后,它会暂停 1 秒,然后再继续处理。 运行 在调试模式下,这完全解决了我们遇到的所有问题。但是,当我将此解决方案编译为发布版本时,我的计时器方法似乎永远停滞不前。
在下面的代码中,我有一个简单的 while 循环,循环直到 bool 为真。 (我不想使用睡眠,因为我不希望线程变得无响应)
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;
}
任何人都可以看到在 运行 处于 Release 模式时 while 循环无限卡住的原因吗?另外,如果有人看到更好的解决方案,请告诉我。不过我必须使用 VS 2010。
感谢阅读。
您的代码中似乎存在竞争条件。启动定时器首先启用定时器,然后将 isWaitOver
设置为 false
。 OnTimedEvent
在运行时将 isWaitOver
设置为 true
。这不太可能,但在繁忙的系统上,计时器可能会在主线程将 isWaitOver
设置为 false
之前触发 OnTimedEvent
。如果发生这种情况,那么 isWaitOver
可能总是在您的循环中显示为 false。为防止出现这种情况,请将 isWaitOver = false
行放在 aTimer.Enabled = true
.
之前
更有可能的问题是优化器对代码中的内容进行了重新排序。如果单个线程不会注意到差异,则允许这样做,但在这样的多线程场景中可能会导致问题。要解决这个问题,您可以制作 isWaitOver
volatile
or put memory barriers in your code. See Threading in C# by Joseph Albahari 来写一篇好文章。
一般来说,当涉及到易失性和内存障碍发挥作用的时候,您已经使您的代码变得复杂和脆弱。 Memory Barriers 是非常高级的东西,非常容易出错并且几乎不可能正确测试(例如,行为因您使用的 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();
}
抱歉,我很困惑。我们有一个向硬件发送命令的进程,该进程被淹没了。我创建了一个简单的解决方案,在每发送 100 次后,它会暂停 1 秒,然后再继续处理。 运行 在调试模式下,这完全解决了我们遇到的所有问题。但是,当我将此解决方案编译为发布版本时,我的计时器方法似乎永远停滞不前。
在下面的代码中,我有一个简单的 while 循环,循环直到 bool 为真。 (我不想使用睡眠,因为我不希望线程变得无响应)
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;
}
任何人都可以看到在 运行 处于 Release 模式时 while 循环无限卡住的原因吗?另外,如果有人看到更好的解决方案,请告诉我。不过我必须使用 VS 2010。
感谢阅读。
您的代码中似乎存在竞争条件。启动定时器首先启用定时器,然后将 isWaitOver
设置为 false
。 OnTimedEvent
在运行时将 isWaitOver
设置为 true
。这不太可能,但在繁忙的系统上,计时器可能会在主线程将 isWaitOver
设置为 false
之前触发 OnTimedEvent
。如果发生这种情况,那么 isWaitOver
可能总是在您的循环中显示为 false。为防止出现这种情况,请将 isWaitOver = false
行放在 aTimer.Enabled = true
.
更有可能的问题是优化器对代码中的内容进行了重新排序。如果单个线程不会注意到差异,则允许这样做,但在这样的多线程场景中可能会导致问题。要解决这个问题,您可以制作 isWaitOver
volatile
or put memory barriers in your code. See Threading in C# by Joseph Albahari 来写一篇好文章。
一般来说,当涉及到易失性和内存障碍发挥作用的时候,您已经使您的代码变得复杂和脆弱。 Memory Barriers 是非常高级的东西,非常容易出错并且几乎不可能正确测试(例如,行为因您使用的 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();
}