Безопасная остановка задачи

Мой вопрос в том, как я могу остановить долговременную задачу (.net 4)? Я внедрил TPL и попытался использовать CancellationTokenSource, но он, похоже, не работает для моего сценария. Все примеры, которые я видел, предполагают, что вы выполняете работу в цикле while, чтобы вы могли проверить, была ли задача отменена, тогда как я просто выполняю одну операцию, которая занимает много времени. Я не могу дождаться завершения работы, поскольку мне нужно предположить, что она никогда не завершится. Вот код, который я пробовал:

        bool? result = null;

        var cs = new CancellationTokenSource();
        var ct = cs.Token;

        var doWorkTask = new Task(() =>
        {
            Console.WriteLine("start dowork task");

            result = Work.LongRunning();
         }, ct);

        doWorkTask.Start();

        Task.WaitAny(new Task[] { doWorkTask }, timetowait);

        if (doWorkTask.IsCompleted)
        {
        Console.WriteLine("dowork task completed");

            doWorkTask.Dispose();
        }
        else
        {
        Console.WriteLine("dowork task has timedout");

            cs.Cancel();

            throw new TimeoutException("Timeout hit.");
        }

Код работает, но задача никогда не удаляется, если происходит "тайм-аут", и работа, которая выполняется, обращается к "неуправляемому коду", то есть к ресурсам. Тем не менее IsCancelledRequested не может использоваться в Work.LongRunning(), поэтому я не могу ThrowIfCancellationRequested.

Я открыт для других идей, а также я попытался BackgroundWorker, но это также похоже не подходит.

Новый пример:

var service = new System.ServiceProcess.ServiceController(ServiceName, ServerName);

        var serviceTask = Task.Factory.StartNew(() =>
        {
            result = (service.Status == ServiceControllerStatus.Running
                 || service.Status == ServiceControllerStatus.StartPending);
        }, cs.Token);

        serviceTask.Wait(2000, cs.Token);

        if (!serviceTask.IsCompleted)
        {
            cs.Cancel();
        }

Ответ 1

Параллельная библиотека задач предназначена для работы с ЦП. Работа с интенсивным процессором выполняется в режиме ожидания. Если ваш Work.LongRunning() интенсивно работает на CPU, вы должны иметь возможность пропускать токен отмены и отменить его. Если это не интенсивность ЦП, вы можете просто отказаться от результата в конечном обратном вызове и не заморачиваться с остановкой фактической работы, так как она просто ждет.

Кстати, если у вас есть ожидание (для вызова базы данных или что-то еще), у вас, вероятно, есть асинхронные методы где-то внизу. Вы можете нарисовать шаблон Begin/End и обернуть его в Task. Этот вопрос объясняет, как: TPL TaskFactory.FromAsync vs Задачи с методами блокировки Таким образом, вы избежите подталкивания потока общего назначения, поскольку ожидаемое выполнение выполняется специальным способом OS.

Ответ 2

Вот пример описанного выше варианта 1 (т.е. просто уничтожение задачи без отмены сигнализации)

class Program
    {
        private static void Main(string[] args)
        {
            Test test = new Test();
            test.Run();

            Console.WriteLine("Type c to cancel");
            if (Console.ReadLine().StartsWith("c"))
            {
                Console.WriteLine("cancellation requested");
                test.CancellationTokenSource.Cancel();
            }

            Console.ReadLine();
        }
    }

    public class Test
    {
        private void DoSomething()
        {
            Console.WriteLine("DoSomething runs for 30 seconds ");
            Thread.Sleep(new TimeSpan(0, 0, 0, 30));
            Console.WriteLine("woke up now ");
        }

        public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();

        public void Run()
        {
                var generateReportsTask = Task.Factory.StartNew(() =>
                {
                    CancellationTokenSource.Token.ThrowIfCancellationRequested();
                    Task doSomething = new Task(DoSomething, CancellationTokenSource.Token);
                    doSomething.Start();

                    doSomething.Wait(CancellationTokenSource.Token);
                }, CancellationTokenSource.Token);

                generateReportsTask.ContinueWith(
                    (t) =>
                    {
                        if (t.Exception != null)
                            Console.WriteLine("Exceptions reported :\n " + t.Exception);

                        if (t.Status == TaskStatus.RanToCompletion)
                            Console.WriteLine("Completed report generation task");
                        if (t.Status == TaskStatus.Faulted)
                            Console.WriteLine("Completed reported generation with unhandeled exceptions");
                        if(t.Status == TaskStatus.Canceled)
                            Console.WriteLine("The Task Has been cancelled");
                    });

        }
    }