可取消后台工作,但不违反SRP
本文关键字:SRP 后台 工作 可取消 | 更新日期: 2023-09-27 18:08:55
在backgroundworker
内部进行长时间运行的测量。由于SRP
(单责任原则),测量不应该知道它正在另一个线程中运行。
让我们考虑一下这个例子:
void MeasurementWorker(object sender, DoWorkEventArgs e)
{
Measurement measurement = new Measurement();
measurement.Execute();
}
如何允许这种模式的测量取消?
编辑:Measurement.Execute
现在是长时间运行的测量方法,应该是可取消的,但它应该测量的SRP
不应该与线程上下文相违背。例如,在没有线程上下文的情况下进行一些测试。
如果您希望您的测量处理是可取消的,您必须让它意识到某种取消标志。另一种选择是以不兼容的方式取消它(abort),但这是非常不鼓励的,因为你可能会在重要的事情中间停止处理,而没有给它清理或释放资源的机会。
代替BackgroundWorker
,你可以使用任务并行库,然后代码看起来像这样:
CancellationTokenSource cts = new CancellationTokenSource();
Task tsk = Task.Factory.StartNew(() =>
{
Measurement measurement = new Measurement();
measurement.Execute(cts.Token);
},
cts.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
其中Execute
可能看起来像这样:
public void Execute(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
while (true)
{
// processing
// ...
// need to cancel?
ct.ThrowIfCancellationRequested();
}
}
取消在主线程中调用:
cts.Cancel();
您将获得TaskCancelledException
,但这是预期的。
或者,如果您不想要异常,请使用以下版本的Execute
。它并不严格遵循TPL指南,但如果您不使用条件延续,它将工作得很好。
public void Execute(CancellationToken ct)
{
if (ct.IsCancellationRequested)
return;
while (true)
{
// processing
if (ct.IsCancellationRequested)
return;
}
}
就像我在评论中说的,我将使用TPL来解决这个问题。这里有一个解决方案,允许取消而不违反SRP:
将。net Framework BackgroundWorker封装在你自己的类中,实现和接口ICancellable,如下所示:
public interface ICancellable
{
bool CancellationPending {get;}
}
public class BackgroundWorkerWrapper : ICancellable
{
private BackgroundWorker _realWorker;
public BackgroundWorkerWrapper(BackgroundWorker realWorker)
{
_realWorker = realWorker;
}
public bool CancellationPending
{
get { return _realWorker.CancellationPending; }
}
}
在DoWork处理程序中执行以下操作:
void MeasurementWorker(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ICancellable cancellable = new BackgroundWorkerWrapper(worker);
Measurement lastMeasurement = new Measurement();
lastMeasurement.Execute(cancellable);
}
现在在您的测量中,您可以使用CancellationPending属性以一种干净的方式检查取消是否被请求。
你说什么?