5

我有一个后台工作人员,它通过 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;
                    }

                }
4

3 回答 3

16

你走错了路。

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

作为解决方案:使用计时器。你有一个周期性的任务,使用正确的工具。

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

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

于 2012-04-23T20:25:58.567 回答
2

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

与任何其他线程同步对象一样,该ManualResetEvent对象具有WaitOne可以等待事件设置或超时的方法。所以通常你可以WaitOne等待你想要等待的数量(这将像 a 一样工作Thread.Sleep)但是主线程可以随时唤醒后台线程。

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

if (bkgwk.CancellationPending)
{
    cancelled = true;
    e.Cancel = true;
    bkgwk.Dispose();
    break;
}
于 2012-04-23T20:28:46.737 回答
2

或者更好的是,使用Reactive Extensions 框架并让“工作人员”观察一系列间隔事件。然后可以轻松地处理流以停止生成新事件,从而终止工作人员。将此与取消一次性使用相结合,您可以使用一个一次性使用来停止间隔计时器和任何工作人员的工作:

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)确保将在调度程序线程上调用订阅。

于 2012-04-23T20:56:15.583 回答