简体   繁体   English

安全地停止长时间运行的任务

[英]Safely stop long running task

My question is how can I stop a long running task (.net 4)? 我的问题是如何停止长时间运行的任务(.net 4)? I have implemented TPL and tried using the CancellationTokenSource but it doesn't seem to work for my scenario. 我已经实现了TPL并尝试使用CancellationTokenSource,但它似乎不适用于我的场景。 All examples I've seen assume you're doing work in a while-loop so that you can check if the task has been cancelled, whereas I just have a single operation that takes long. 我见过的所有例子都假设你在while循环中工作,这样你就可以检查任务是否已被取消,而我只需要一个需要很长时间的操作。 I cannot wait for the work to be completed as I need to assume it might never complete. 我不能等待工作完成,因为我需要假设它可能永远不会完成。 Here is the code I have tried: 这是我尝试过的代码:

        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.");
        }

The code works but the task is never disposed if the “timeout” happens and the work that is being done accesses “unmanaged code” ie resources. 代码可以工作,但是如果“超时”发生并且正在完成的工作访问“非托管代码”即资源,则永远不会处理任务。 That said the IsCancelledRequested cannot be used in Work.LongRunning() so I cannot ThrowIfCancellationRequested. 那说IsCancelledRequested不能在Work.LongRunning()中使用,所以我不能ThrowIfCancellationRequested。

I am open to other ideas as well as I have tried BackgroundWorker but that also doesn't seem to fit. 我对其他想法持开放态度,并且我尝试过BackgroundWorker,但这似乎也不合适。

New example: 新例子:

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

Here is an example for option 1 described obove ( ie just killing the Task without signalling cancellation) 这是一个描述的选项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");
                    });

        }
    }

Task Parallel Library is designed for CPU intensive work. 任务并行库专为CPU密集型工作而设计。 CPU intensive work is done in a while look. CPU密集型工作一蹴而就。 If your Work.LongRunning() is CPU intensive you should be able to pass the cancellation token inside and cancel it. 如果您的Work.LongRunning()是CPU密集型的,您应该能够在内部传递取消令牌并取消它。 If it is not CPU intensive then you can simply discard the result in an eventual callback and not bother with stopping the actual work since it is just waiting. 如果它不是CPU密集型,那么你可以简单地丢弃最终回调的结果,而不是因为它只是在等待而停止实际工作。

BTW if you have waiting (for database call or something) you probably have asynchronous methods somewhere at the bottom. 顺便说一句,如果你有等待(数据库调用或其他东西),你可能在底部的某个地方有异步方法。 You can surface the Begin/End pattern and wrap it in a Task. 您可以显示Begin / End模式并将其包装在Task中。 This question explains how: TPL TaskFactory.FromAsync vs Tasks with blocking methods This way you will avoid hogging a general purpose thread since IO waiting is done in a special way handled by the OS. 这个问题解释了如何: TPL TaskFactory.FromAsync vs具有阻塞方法的任务这种方式可以避免占用通用线程,因为IO等待是以OS处理的特殊方式完成的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM