我有一个后台工作者,它通过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;
}
}
答案 0 :(得分:15)
你走错了路。
Sleep()
。当然不会超过几毫秒。 Sleep(1)
是对CPU的一大浪费。你正在伤害你的其他线程。至少考虑将其提升到Sleep(100)
左右。找到允许取消的最大延迟。 作为解决方案:使用计时器。你有一个周期性的任务,使用正确的工具。
使用System.Threading.Timer
进行基于ThreadPool的后台工作。通过Dispatcher.Invoke()更新GUI。
使用Dispatcher.Timer
在主线程上执行小的(不到0.1秒)工作。您可以直接更新GUI。
答案 1 :(得分:2)
您可以使用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;
}
答案 2 :(得分:2)
甚至更好,使用the Reactive Extensions framework并让“工人”观察间隔事件流。然后可以容易地处理流以停止生成新事件,从而终止工作人员。将其与一次性取消相结合,您可以使用一个一次性停止间隔计时器和任何工作人员进行工作:
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)
确保将在调度程序线程上调用订阅。