在检查取消时让后台工作线程进入睡眠状态

Send a backgroundworker to sleep while checking for cancellation

提问人:Damo 提问时间:4/24/2012 最后编辑:Damo 更新时间:3/11/2015 访问量:15393

问:

我有一个后台工作人员,它通过 ReportProgress 定期更新 GUI。

更新定期进行,例如每 5 秒更新一次,也可能是 20 秒。为了在设定的时间执行更新,我将工作进程发送到休眠状态,当它唤醒时,它会使用新信息更新 GUI。

工作线程支持取消,并且在睡眠之外,它会正确取消。

我希望能够在等待期间调用取消,但是将线程送入睡眠状态使这是不可能的。

我假设我必须调用一个循环并检查取消作为循环的一部分,以模拟线程睡眠。

实现这一目标的最佳方法是什么,我的时机与我的尝试完全不符。

            long counter = 0;
            long sleepfor = timelinespeed*1000;
            int timelinespeed = 10;

                while (counter != sleepfor)
                {

                    Thread.Sleep(1);

                    counter++;

                    if (bkgwk.CancellationPending)
                    {
                        cancelled = true;
                        e.Cancel = true;
                        bkgwk.Dispose();
                        break;
                    }

                }
C# WPF 多线程 backgroundworker

评论


答:

17赞 ℍ ℍ 4/24/2012 #1

你走错了路。

  • Bgw 正在使用 ThreadPool,因此应该用于(相对)短的操作,通常在 0.5 秒以内。不建议无限期地保留一个。
  • 您几乎不应该在任何线程上使用。当然不会超过几毫秒。Sleep()
  • 在繁忙等待 5 秒中使用是 CPU 的一大浪费。你正在伤害你的其他线程。至少考虑将其提高到左右。找到您允许取消的最大延迟。Sleep(1)Sleep(100)

解决方案:使用计时器。您有定期任务,请使用正确的工具。

用于基于 ThreadPool 的后台工作。通过 Dispatcher.Invoke() 更新 GUI。System.Threading.Timer

使用 在主线程上执行小部分(不到 0.1 秒)的工作。您可以直接更新 GUI。Dispatcher.Timer

评论

0赞 JMarsch 4/24/2012
+1 计时器建议。如果您使用的是 wpf,则还可以查看 Dispatcher 计时器。取消变得很容易 -- 你只需停止计时器(假设允许任何当前任务完成是可以的)
0赞 Damo 4/24/2012
System.Threading.Timer 是否允许我使用信息更新 GUI?MSDN 建议使用 System.Windows.Forms.Timer,但这是单线程的?
0赞 ℍ ℍ 4/24/2012
Forms.Timer 和 Dispatcher.Timer 都在主线程上运行,因此我不会将它们用于繁重的操作。从其他线程更新 GUI 是相同的:使用 Dispatcher.Invoke()
2赞 shf301 4/24/2012 #2

可以使用 ManualResetEvent 来允许线程在不轮询的情况下等待,并允许另一个线程随时唤醒它。

以及任何其他线程同步对象都具有一个可以等待事件设置或超时的方法。因此,通常您可以等待要等待的量(这将像 ) 一样工作,但主线程可以随时唤醒后台线程。ManualResetEventWaitOneWaitOneThread.Sleep

ManualResetEvent backgroundWakeEvent = new ManualResetEvent(false);
....
bkgwk.CancelAync();
backgroundWaitEvent.Set();
....
backgroundWakeEvent.WaitOne(5000);

if (bkgwk.CancellationPending)
{
    cancelled = true;
    e.Cancel = true;
    bkgwk.Dispose();
    break;
}
2赞 Peter Lillevold 4/24/2012 #3

或者更好的是,使用响应式扩展框架,让“工作者”观察一系列间隔事件。然后,可以很容易地释放流以停止生成新事件,从而终止工作线程。将其与一次性取消设备相结合,您就有了一次性设备来停止间隔计时器和任何工作人员的工作:

var cancel = new BooleanDisposable();
var subscription = Observable.Interval(TimeSpan.FromSeconds(20))
                             .ObserveOn(Scheduler.DispatcherScheduler)
                             .Subscribe(i => PerformWorkOnUI(cancel));
var uiWork = new CompositeDisposable(cancel, subscription);

若要取消计时器事件流,只需释放复合取消/订阅:

uiWork.Dispose();

说明:确保在调度程序线程上调用订阅。ObserveOn(Scheduler.DispatcherScheduler)