Mike
Mike

Reputation: 11

Multi Threading, and cancellation token in C#

I am newbie to multi threading and wasted couple of days debugging this program with no luck.I am trying to do proof of concept before I go ahead and implement this for production.

I have a list of custom objects. Their properties are T of type Task and Timeout of type int. I am trying to lunch multiple the task in parrallel and terminate the task if it times out. For demonstration purposes, I did create two simple tasks which they do simple things

  public class Program
{
    public static void Main(string[] args)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        CancellationToken cancellationToken = cancellationTokenSource.Token;

        List<MyItem> myItems = new List<MyItem>();
        myItems.Add(createtask1(2000,1000,cancellationToken)); // this task should run for one second....result: should timeout
        myItems.Add(createtask2(2000,3000,cancellationToken));// this task should run for two second....result: should NOT timeout

        Parallel.ForEach(myItems,(item)=>{

           Task task = item.T;

            // start the task
            task.Start();

            Console.WriteLine("waiting for the task with timeout:{0} to finish", item.Timeout);
            try{
                bool hasNotTimedOut = task.Wait(item.Timeout);

                if (hasNotTimedOut)
                {
                    Console.WriteLine("Task with timeout: {0}, has not timed out", item.Timeout);
                }
                else
                {
                    Console.WriteLine("Task with timeout: {0}, has timed out", item.Timeout);
                        cancellationTokenSource.Cancel();
                        Console.WriteLine("Status of the task with timeout: {0} is {1}", item.Timeout,task.Status.ToString());

                }
            }
            catch (AggregateException ex)
            {
                if (ex.InnerException is OperationCanceledException)
                {
                    Console.WriteLine("Status of the task with timeout: {0} is {1}5", item.Timeout,task.Status.ToString());

                }
                else
                    Console.WriteLine(ex);
            }



            });

        //    Task.WaitAll(myItems.Select(item=>item.T).ToArray());        

        Console.WriteLine("End of Main");
    }

    private static MyItem createtask1(int delayTime, int timeout,CancellationToken cancellationToken)
    {
        MyItem toReturn = new MyItem();
        toReturn.Timeout = timeout;
        toReturn.T = new Task(()=>{
                              Console.WriteLine("Task1 is getting executed with DelayTime:{0}, timeout:{1}",delayTime,timeout);
                              Task.Delay(delayTime).Wait();
                              Console.WriteLine("Task1 finished executing with DelayTime:{0}, timeout:{1}",delayTime,timeout);
            cancellationToken.ThrowIfCancellationRequested();
                              },cancellationToken);


        return toReturn;                     

    }
    private static MyItem createtask2(int delayTime, int timeout,CancellationToken cancellationToken)
    {
        MyItem toReturn = new MyItem();
        toReturn.Timeout = timeout;
        toReturn.T = new Task(()=>{
                              Console.WriteLine("Task2 is getting executed with DelayTime:{0}, timeout:{1}",delayTime,timeout);
                              Task.Delay(delayTime).Wait();
                              Console.WriteLine("Task2 finished executing with DelayTime:{0}, timeout:{1}",delayTime,timeout);
            cancellationToken.ThrowIfCancellationRequested();
                              },cancellationToken);
        return toReturn;                     

    }
}

public class MyItem{
    public Task T{get;set;}
    public int Timeout{get;set;}
}

It seems it is trying to cancel the last task, but in my case the first case should be cancelled. My code works perfectly if i dont include the CancellationToken i.e it tells me what task timed out.

Can you please tell me what I am doing wrong

Upvotes: 1

Views: 1799

Answers (1)

M&#225;rton
M&#225;rton

Reputation: 31

Just because you pass a CancellationToken as an argument to a Task and then cancel the Token, the Task will still not be cancelled. You need to check the Token inside the function, and manually cancel the Task when requested.

A CancellationToken can be passed to Task.Delay(), which implements this system.

Like so:

private static MyItem createtask1(int delayTime, int timeout, CancellationToken cancellationToken)
    {
        MyItem toReturn = new MyItem();
        toReturn.Timeout = timeout;
        toReturn.T = new Task(() => {
            Console.WriteLine("Task1 is getting executed with DelayTime:{0}, timeout:{1}", delayTime, timeout);
            Task.Delay(delayTime, cancellationToken).Wait();
            Console.WriteLine("Task1 finished executing with DelayTime:{0}, timeout:{1}", delayTime, timeout);
            cancellationToken.ThrowIfCancellationRequested();
        }, cancellationToken);


        return toReturn;

    }

Also, you don't need cancellationToken.ThrowIfCancellationRequested();, because waiting for Task.Delay() throws an exception if it is cancelled.

Upvotes: 1

Related Questions