Как реализовать отменяемый рабочий поток - PullRequest
5 голосов
/ 31 мая 2010

Я пытаюсь реализовать отменяемый рабочий поток, используя новые конструкции потоков в пространстве имен System.Threading.Tasks. До сих пор я придумал эту реализацию:

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

Когда Stop() вернется, я ожидаю, что Worker.Status будет TaskStatus.Canceled.
Мои модульные тесты показали, что при определенных условиях Worker.Status остается установленным на TaskStatus.Running.

Это правильный способ реализации отменяемого рабочего потока?

1 Ответ

5 голосов
/ 31 мая 2010

Я считаю, что проблема в вашем звонке на

Worker.Wait(_cancellationTokenSource.Token);

Он ждет, пока токен не будет сигнализирован - это уже так, потому что вы только что позвонили Cancel(). Если вы измените это просто

Worker.Wait();

тогда я полагаю, вы увидите состояние RanToCompletion. Вы не увидите Отменено, потому что ваша задача не выбрасывает OperationCanceledException. Если вы измените свой метод RunTasks на вызов

cancellationToken.ThrowIfCancellationRequested()

в конце, тогда вам нужно поймать AggregateException в Stop - но тогда вы увидите состояние Canceled в конце.

По крайней мере, так показывают мои эксперименты:)

...