16

How can I stop a long running task (.net 4)?

I have implemented TPL and tried using the CancellationTokenSource but it doesn’t seem to work for my scenario. 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. 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' i.e. resources. That said the IsCancelledRequested cannot be used in Work.LongRunning() so I cannot ThrowIfCancellationRequested.

I am open to other ideas as well as I have tried BackgroundWorker but that also doesn’t seem to fit.

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();
}
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
nickv
  • 161
  • 1
  • 1
  • 4

2 Answers2

4

Here is an example for option 1 described obove ( i.e. just killing the Task without signalling cancellation)

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

        }
    }
reVerse
  • 35,075
  • 22
  • 89
  • 84
Avi Ben-Margi
  • 167
  • 1
  • 3
  • 5
    In fact, this code doesn't kill the long running doSomething task. When you press "c", you'll get the "The Task Has been cancelled" but the doSomething task still runs. Just wait 30 seconds and you'll get "woke up now " text in the console. – Peter Macej Apr 12 '17 at 09:10
2

Task Parallel Library is designed for CPU intensive work. CPU intensive work is done in a while look. If your Work.LongRunning() is CPU intensive you should be able to pass the cancellation token inside and cancel it. 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.

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

Community
  • 1
  • 1
Stilgar
  • 22,354
  • 14
  • 64
  • 101
  • Thanks for the response! I looked at the Begin\End pattern, but then to check on a result on the End's ((Task)isyncResult).Result you have to wait untill it completes. I made an update on my example. As the properties on the ServiceController seems to be lazy loaded as soon as you check a value it might take how ever long... – nickv May 02 '12 at 14:16
  • I must admit that I can't follow your code completely but why aren't you adding continuations with ContinueWith? – Stilgar May 02 '12 at 14:45
  • Because i dont want to continue with a new task. I have one task that i want perform, that task might not hield a response due to whatever so the task will be in a running state forever even though Cancelled was called. – nickv May 02 '12 at 14:51
  • Do you need to do anything when the task completes successfully? If you do then you need to continue with another task. If you don't then don't do anything (since the task is not CPU intensive). – Stilgar May 02 '12 at 15:44