可取消后台工作,但不违反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不应该与线程上下文相违背。例如,在没有线程上下文的情况下进行一些测试。

可取消后台工作,但不违反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属性以一种干净的方式检查取消是否被请求。

你说什么?