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

16

如何停止一个长时间运行的任务(.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.");
}
代码可以正常工作,但如果超时发生并且正在进行的工作访问"非托管代码"即资源,则任务永远不会被处理。也就是说,在Work.LongRunning()中无法使用IsCancelledRequested,因此我无法使用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();
}
2个回答

4
这里有一个关于选项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");
                    });

        }
    }

6
实际上,这段代码并没有停止长时间运行的"doSomething"任务。当你按下 "c" 键时,会收到 "The Task Has been cancelled" 的提示,但是"doSomething"任务仍在运行。只需等待30秒,你将在控制台中看到 "woke up now " 文字。 - Peter Macej

2

任务并行库是为CPU密集型工作设计的。CPU密集型工作是在while循环中完成的。如果你的Work.LongRunning()是CPU密集型的,你应该能够在内部传递取消令牌并取消它。如果它不是CPU密集型的,那么你可以简单地在最终回调中丢弃结果,不必担心停止实际工作,因为它只是等待。

顺便说一句,如果你有等待(例如数据库调用),你可能在底层有异步方法。你可以暴露Begin/End模式并将其包装在一个任务中。这个问题解释了如何做:TPL TaskFactory.FromAsync vs Tasks with blocking methods 这样你就可以避免占用通用线程,因为IO等待是通过操作系统以特殊方式处理的。


谢谢回复!我看了一下Begin\End模式,但是要检查End的结果((Task<bool?>)isyncResult).Result,你必须等待它完成。我在我的示例中进行了更新。由于ServiceController上的属性似乎是延迟加载的,只要你检查一个值,可能需要很长时间... - nickv
我必须承认,我无法完全理解你的代码,但为什么你不使用ContinueWith添加续体呢? - Stilgar
因为我不想继续进行新的任务。我有一个任务要执行,由于某种原因,该任务可能不会产生响应,因此即使调用了“取消”,该任务也将永远处于运行状态。 - nickv
任务成功完成后,您需要做任何事情吗?如果需要,那么您需要继续执行另一个任务。如果不需要,则不要做任何事情(因为该任务不占用CPU)。 - Stilgar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接