Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement cancellable worker thread

I'm trying to implement a cancellable worker thread using the new threading constructs in System.Threading.Tasks namespace. So far I have have come up with this implementation:

public sealed class Scheduler
{
    private CancellationTokenSource _cancellationTokenSource;
    public System.Threading.Tasks.Task Worker { get; private set; }

    public void Start()
    {
        _cancellationTokenSource = new CancellationTokenSource();

        Worker = System.Threading.Tasks.Task.Factory.StartNew(
            () => RunTasks(_cancellationTokenSource.Token),
             _cancellationTokenSource.Token
        );
    }

    private static void RunTasks(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Thread.Sleep(1000); // simulate work
        }
    }

    public void Stop()
    {
        try
        {
            _cancellationTokenSource.Cancel();
            Worker.Wait(_cancellationTokenSource.Token);
        }
        catch (OperationCanceledException)
        {
            // OperationCanceledException is expected when a Task is cancelled.
        }
    }
}

When Stop() returns I expect Worker.Status to be TaskStatus.Canceled.
My unit tests have shown that under certain conditions Worker.Status remains set to TaskStatus.Running.

Is this a correct way to implement a cancellable worker thread?

like image 445
Arnold Zokas Avatar asked May 31 '10 09:05

Arnold Zokas


1 Answers

I believe that the problem is in your call to

Worker.Wait(_cancellationTokenSource.Token);

That's waiting for the token to be signalled - which it already is, because you've just called Cancel(). If you change that to just

Worker.Wait();

then I believe you'll see a state of RanToCompletion. You won't see Canceled, because your task isn't throwing OperationCanceledException. If you change your RunTasks method to call

cancellationToken.ThrowIfCancellationRequested()

at the end, then you'll need to catch an AggregateException in Stop - but then you'll see a state of Canceled at the end.

At least, that's what my experimentation shows :)

like image 187
Jon Skeet Avatar answered Sep 22 '22 18:09

Jon Skeet