使用带 Thread.Sleep 的 BackgroundWorker 的奇怪行为
Odd behavior using BackgroundWorker with Thread.Sleep
我以前从未见过这样的东西。我正在使用 Visual Studio 2015 预览版并在 Visual Studio 2012 上对其进行了验证。
private void InitializeBackgroundWorkers()
{
MONITOR.WorkerReportsProgress = true;
MONITOR.WorkerSupportsCancellation = true;
MONITOR.DoWork += new DoWorkEventHandler(MONITOR_DoWork);
MONITOR.ProgressChanged += new ProgressChangedEventHandler(MONITOR_ProgressChanged);
MONITOR.RunWorkerCompleted += new RunWorkerCompletedEventHandler(MONITOR_WorkCompleted);
MONITOR2.WorkerReportsProgress = true;
MONITOR2.WorkerSupportsCancellation = true;
MONITOR2.DoWork += new DoWorkEventHandler(MONITOR2_DoWork);
MONITOR2.ProgressChanged += new ProgressChangedEventHandler(MONITOR2_ProgressChanged);
}
问题出现在 BackgroundWorker 的 DoWork 事件中。显然我不知何故犯了一个错误。
private void MONITOR2_DoWork(object sender, DoWorkEventArgs e)
{
while (CONTINUE)
{
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
//BP2
clockNow = RunTime.ElapsedTicks;
TS = RunTime.Elapsed;
MONITOR2.ReportProgress(0);
}
RunTime.Stop();
}
问题是由于各种原因我注意到 ClockThen 总是 "newer" 比 ClockNow。直到现在我都忽略了它并更改了我的代码以将 ClockThen 用作 ClockNow,反之亦然。
我终于有时间用断点仔细查看代码了。我在 clockThen 赋值处放置了一个,在 clockNow 赋值处放置了一个,在 ReportProgress 事件中放置了一个,因此我可以在 MONITOR2 堆栈之外再停止一次。
注意:我正在使用 ProgressChanged 作为更新主要 UI.
的一种方式
private void MONITOR2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
double calcClockOld = (((double)oldClockNow) * (1F / Stopwatch.Frequency));
double calcClockThen = (((double)clockThen) * (1F / Stopwatch.Frequency));
double calcClockNow = (((double)clockNow) * (1F / Stopwatch.Frequency));
//BP3
tbxClockDrift.Text = ((calcClockThen - calcClockNow) * 1000000).ToString("0.#####\ µs");
string timestamp = String.Format("{0:00}:{1:00}:{2:00}", TS.Hours, TS.Minutes, TS.Seconds);
tbxTime.Text = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString();
tbxRunTime.Text = timestamp;
tbxTimeZone.Text = TimeZone.CurrentTimeZone.ToUniversalTime(DateTime.Now).ToString();
}
第三个断点在 tbxClockDrift.Text 赋值处。
所以我总共有 3 个断点,但是当逐步执行代码时,BP1 总是执行两次,我相信这就是为什么值一直更高的原因!
所以我看到这是一个幻影断点:
步进 1,clockThen = RunTime.ElapsedTicks;
步进 2,跳转到 clockNow = Runtime.ElapsedTicks;睡了一秒钟后。
Step Through 3, back to clockThen assignment (verified new values!)
步进 4,tbxClockDrift.Text 作业!
为什么clockThen收到了第二次赋值!?
如果我稍微重写您的方法以使睡眠成为 while 循环内发生的最后一件事,问题会更明显一些。此代码的行为与您当前的代码完全相同。
private void MONITOR2_DoWork(object sender, DoWorkEventArgs e)
{
if(CONTINUE)
{
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
while (true)
{
//BP2
clockNow = RunTime.ElapsedTicks;
TS = RunTime.Elapsed;
MONITOR2.ReportProgress(0);
if(!CONTINUE)
break;
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
}
}
RunTime.Stop();
}
所以 clockThen
没有收到第二份作业,只是 clockThen
是你睡觉前做的最后一件事。此外,ReportProgress
的处理不必在您调用该函数时就发生,调用该函数只是将事件排队,以便下次 UI 可以解决它时触发。
所以发生的事情是你分配 clockNow
,排队进度报告,你分配一个值给 clockThen
,最后你开始睡觉。在你睡觉的时候 ReportProgress
终于到了 运行 并且你得到了你在睡觉前分配的 clockThen
的值。
我以前从未见过这样的东西。我正在使用 Visual Studio 2015 预览版并在 Visual Studio 2012 上对其进行了验证。
private void InitializeBackgroundWorkers()
{
MONITOR.WorkerReportsProgress = true;
MONITOR.WorkerSupportsCancellation = true;
MONITOR.DoWork += new DoWorkEventHandler(MONITOR_DoWork);
MONITOR.ProgressChanged += new ProgressChangedEventHandler(MONITOR_ProgressChanged);
MONITOR.RunWorkerCompleted += new RunWorkerCompletedEventHandler(MONITOR_WorkCompleted);
MONITOR2.WorkerReportsProgress = true;
MONITOR2.WorkerSupportsCancellation = true;
MONITOR2.DoWork += new DoWorkEventHandler(MONITOR2_DoWork);
MONITOR2.ProgressChanged += new ProgressChangedEventHandler(MONITOR2_ProgressChanged);
}
问题出现在 BackgroundWorker 的 DoWork 事件中。显然我不知何故犯了一个错误。
private void MONITOR2_DoWork(object sender, DoWorkEventArgs e)
{
while (CONTINUE)
{
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
//BP2
clockNow = RunTime.ElapsedTicks;
TS = RunTime.Elapsed;
MONITOR2.ReportProgress(0);
}
RunTime.Stop();
}
问题是由于各种原因我注意到 ClockThen 总是 "newer" 比 ClockNow。直到现在我都忽略了它并更改了我的代码以将 ClockThen 用作 ClockNow,反之亦然。
我终于有时间用断点仔细查看代码了。我在 clockThen 赋值处放置了一个,在 clockNow 赋值处放置了一个,在 ReportProgress 事件中放置了一个,因此我可以在 MONITOR2 堆栈之外再停止一次。
注意:我正在使用 ProgressChanged 作为更新主要 UI.
的一种方式 private void MONITOR2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
double calcClockOld = (((double)oldClockNow) * (1F / Stopwatch.Frequency));
double calcClockThen = (((double)clockThen) * (1F / Stopwatch.Frequency));
double calcClockNow = (((double)clockNow) * (1F / Stopwatch.Frequency));
//BP3
tbxClockDrift.Text = ((calcClockThen - calcClockNow) * 1000000).ToString("0.#####\ µs");
string timestamp = String.Format("{0:00}:{1:00}:{2:00}", TS.Hours, TS.Minutes, TS.Seconds);
tbxTime.Text = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToLongTimeString();
tbxRunTime.Text = timestamp;
tbxTimeZone.Text = TimeZone.CurrentTimeZone.ToUniversalTime(DateTime.Now).ToString();
}
第三个断点在 tbxClockDrift.Text 赋值处。
所以我总共有 3 个断点,但是当逐步执行代码时,BP1 总是执行两次,我相信这就是为什么值一直更高的原因!
所以我看到这是一个幻影断点:
步进 1,clockThen = RunTime.ElapsedTicks;
步进 2,跳转到 clockNow = Runtime.ElapsedTicks;睡了一秒钟后。
Step Through 3, back to clockThen assignment (verified new values!)
步进 4,tbxClockDrift.Text 作业!
为什么clockThen收到了第二次赋值!?
如果我稍微重写您的方法以使睡眠成为 while 循环内发生的最后一件事,问题会更明显一些。此代码的行为与您当前的代码完全相同。
private void MONITOR2_DoWork(object sender, DoWorkEventArgs e)
{
if(CONTINUE)
{
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
while (true)
{
//BP2
clockNow = RunTime.ElapsedTicks;
TS = RunTime.Elapsed;
MONITOR2.ReportProgress(0);
if(!CONTINUE)
break;
oldClockNow = clockNow;
//BP1
clockThen = RunTime.ElapsedTicks;
Thread.Sleep(1000);
}
}
RunTime.Stop();
}
所以 clockThen
没有收到第二份作业,只是 clockThen
是你睡觉前做的最后一件事。此外,ReportProgress
的处理不必在您调用该函数时就发生,调用该函数只是将事件排队,以便下次 UI 可以解决它时触发。
所以发生的事情是你分配 clockNow
,排队进度报告,你分配一个值给 clockThen
,最后你开始睡觉。在你睡觉的时候 ReportProgress
终于到了 运行 并且你得到了你在睡觉前分配的 clockThen
的值。