Having a long running measurement inside a backgroundworker. The measurement should not know that it is running in another thread because of SRP(Single reponsibility principle).

Let's consider this for example:

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

How to allow a cancellation of the measurement such a pattern?

Edit: Measurement.Execute is now to long running measurement method which should be cancelable but it should the SRP of the measurement should not be violated with threading context. For example to do some testing without a threading context.

有帮助吗?

解决方案

If you want your measurement processing to be cancellable you must make it aware of some sort of cancellation flag. Alternative is to cancel it (abort) in an non-compliant way but that is highly discouraged because you could stop your processing in a middle of something important and without giving it a chance to clean up or free resources.

Instead of BackgroundWorker you could use Task Parallel Library and then code could look like this:

CancellationTokenSource cts = new CancellationTokenSource();
Task tsk = Task.Factory.StartNew(() =>
                                      {
                                          Measurement measurement = new Measurement();
                                          measurement.Execute(cts.Token);
                                      }, 
                                      cts.Token, 
                                      TaskCreationOptions.LongRunning,
                                      TaskScheduler.Default);

where Execute might look something like this:

public void Execute(CancellationToken ct)
{
    ct.ThrowIfCancellationRequested();

    while (true)
    {
        // processing
        // ...

        // need to cancel?
        ct.ThrowIfCancellationRequested();
    }
}

To cancel call this in main thread:

cts.Cancel();

You will get TaskCancelledException but that is expected.

Alternatively, if you do not want exception use the following version of Execute. It is not strictly per TPL guidelines but it will work fine if you do not use conditional continuations.

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

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

其他提示

Like I said in my comments I would use the TPL for this problem. Here is a solution that allows cancellation without violating the SRP:

Wrap .NET Framework BackgroundWorker in your own class that implements and interface ICancellable as follows:

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; }
    }
}

In your DoWork handler do the following:

void MeasurementWorker(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    ICancellable cancellable = new BackgroundWorkerWrapper(worker);
    Measurement lastMeasurement = new Measurement();
    lastMeasurement.Execute(cancellable);
}

Now in your Measurement you can check if cancel was requested in a clean way using the CancellationPending Property.

What you say?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top