可取消的背景工作者但不违反SRP

时间:2013-04-13 16:29:06

标签: c# .net backgroundworker single-responsibility-principle

backgroundworker内进行长时间运行的测量。由于SRPSingle reponsibility principle),衡量应该知道它正在另一个线程中运行。

让我们考虑一下这个例子:

void MeasurementWorker(object sender, DoWorkEventArgs e)
{
   Measurement measurement = new Measurement();
   measurement.Execute();
}

如何取消测量这样的模式?

编辑:Measurement.Execute现在是长期运行的测量方法,该方法应该是可取消的,但是应该使用线程上下文来违反测量的SRP。例如,在没有线程上下文的情况下进行一些测试。

2 个答案:

答案 0 :(得分:2)

就像我在评论中说的那样,我会使用TPL来解决这个问题。这是一个允许在不违反SRP的情况下取消的解决方案:

将.NET Framework Ba​​ckgroundWorker包装在您自己的类中,实现和接口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属性检查是否以干净的方式请求取消。

你说什么?

答案 1 :(得分:2)

如果您希望您的测量处理可以取消,您必须让它知道某种取消标记。另一种方法是以不合规的方式取消它(中止),但这是非常气馁的,因为你可以在重要的事情中停止处理,而不给它机会清理或释放资源。

而不是BackgroundWorker您可以使用Task Parallel Library,然后代码可能如下所示:

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指南,但如果您不使用条件continuations,它将正常工作。

public void Execute(CancellationToken ct)
{
    if (ct.IsCancellationRequested)
        return;

    while (true)
    {
        // processing
        if (ct.IsCancellationRequested)
            return;
    }
}