提问人:Damo 提问时间:4/24/2012 最后编辑:Damo 更新时间:3/11/2015 访问量:15393
在检查取消时让后台工作线程进入睡眠状态
Send a backgroundworker to sleep while checking for cancellation
问:
我有一个后台工作人员,它通过 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;
}
}
答:
你走错了路。
- Bgw 正在使用 ThreadPool,因此应该用于(相对)短的操作,通常在 0.5 秒以内。不建议无限期地保留一个。
- 您几乎不应该在任何线程上使用。当然不会超过几毫秒。
Sleep()
- 在繁忙等待 5 秒中使用是 CPU 的一大浪费。你正在伤害你的其他线程。至少考虑将其提高到左右。找到您允许取消的最大延迟。
Sleep(1)
Sleep(100)
解决方案:使用计时器。您有定期任务,请使用正确的工具。
用于基于 ThreadPool 的后台工作。通过 Dispatcher.Invoke() 更新 GUI。System.Threading.Timer
使用 在主线程上执行小部分(不到 0.1 秒)的工作。您可以直接更新 GUI。Dispatcher.Timer
评论
可以使用 ManualResetEvent 来允许线程在不轮询的情况下等待,并允许另一个线程随时唤醒它。
以及任何其他线程同步对象都具有一个可以等待事件设置或超时的方法。因此,通常您可以等待要等待的量(这将像 ) 一样工作,但主线程可以随时唤醒后台线程。ManualResetEvent
WaitOne
WaitOne
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;
}
或者更好的是,使用响应式扩展框架,让“工作者”观察一系列间隔事件。然后,可以很容易地释放流以停止生成新事件,从而终止工作线程。将其与一次性取消设备相结合,您就有了一次性设备来停止间隔计时器和任何工作人员的工作:
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)
评论