Blog Archives

Exiting Tasks (advanced)

In the post “exiting Threads” (https://csharphardcoreprogramming.wordpress.com/2013/12/03/exiting-threads/) we were using a parameter class to define an exitFlag (boolean) and tell a thread when to end. There is a similar boolean for tasks supported by the Microsoft Framework. The class that contains this flag is called CancellationToken.

static void ArbitraryTask1(CancellationToken xCancellationToken) {
    while (!xCancellationToken.IsCancellationRequested) {
        Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));
        Thread.Sleep(1000);
    }

    Console.WriteLine("task says good-bye!");
} //

static void CancelTask1() {
    CancellationTokenSource lCancellationTokenSource = new CancellationTokenSource();
    CancellationToken t = lCancellationTokenSource.Token;

    Task lTask = Task.Factory.StartNew(() => ArbitraryTask1(t), t);

    Thread.Sleep(5000);
    lCancellationTokenSource.Cancel();

    Thread.Sleep(1000);
} //

The program starts a task, waits 5 seconds and then cancels the task. This is straight forward.
In the next example we exit a task by throwing an exception. This is a valid solution. But in general you should avoid exceptions as much as possible. As the name says it already: It is for exceptional situations and not for general solutions. If you use exceptions and catch() blocks frequently in your code, you will definitely slow down your performance. Throwing an exception is very time-consuming.
Nevertheless the TPL is extensively using exceptions. For instance in connection with PLINQ you will actually need to throw exceptions in order to properly report back to the user.

static void ArbitraryTask2(CancellationToken xCancellationToken) {
    ArbitraryTask1(xCancellationToken);

    xCancellationToken.ThrowIfCancellationRequested();
} //

static void CancelTask2() {
    CancellationTokenSource lCancellationTokenSource = new CancellationTokenSource();
    CancellationToken t = lCancellationTokenSource.Token;

    Task lTask = Task.Factory.StartNew(() => ArbitraryTask2(t), t);

    Thread.Sleep(5000);

    try {
        lCancellationTokenSource.Cancel();
        lTask.Wait();
    }
    catch (AggregateException e) {
        Console.WriteLine("Exception message: " + e.InnerExceptions[0].Message);
    }

    Console.ReadLine();
} //

And the following is important. In fact not throwing an exception causes unexpected behavior. Unless I misunderstood something here, this seems to be a serious issue.

static void CancelTask3() {
    CancellationTokenSource lCancellationTokenSource = new CancellationTokenSource();
    CancellationToken lToken = lCancellationTokenSource.Token;

    Task lTask = Task.Factory.StartNew(() => ArbitraryTask1(lToken), lToken);
    lTask.ContinueWith((x) => { Console.WriteLine("task cancelled"); }, TaskContinuationOptions.OnlyOnCanceled);
    lTask.ContinueWith((x) => { Console.WriteLine("task completed"); }, TaskContinuationOptions.OnlyOnRanToCompletion);

    //Thread.Sleep(5000);

    lCancellationTokenSource.Cancel();

    Console.ReadLine();
} //

example output:
task cancelled

The output is exactly what we expect. But now let’s uncomment “Thread.Sleep(5000);”. Suddenly we get something this:

example output:
18:07:37
18:07:38
18:07:39
18:07:40
18:07:41
task says good-bye!
task completed

The first 6 lines are fine. But then we get “task completed”. And all we did was adding one sleep() method in the code. If you have any idea what is causing it please leave a comment.
All I can say here is that throwing an exception solves the problem.

static void CancelTask3() {
    CancellationTokenSource lCancellationTokenSource = new CancellationTokenSource();
    CancellationToken lToken = lCancellationTokenSource.Token;

    //Task lTask = Task.Factory.StartNew(() => ArbitraryTask1(lToken), lToken);  // without throwing an exception
    Task lTask = Task.Factory.StartNew(() => ArbitraryTask2(lToken), lToken);  // with throwing an exception
    lTask.ContinueWith((x) => { Console.WriteLine("task cancelled"); }, TaskContinuationOptions.OnlyOnCanceled);
    lTask.ContinueWith((x) => { Console.WriteLine("task completed"); }, TaskContinuationOptions.OnlyOnRanToCompletion);

    Thread.Sleep(5000);

    lCancellationTokenSource.Cancel();

    Console.ReadLine();
} //

output example:
18:13:30
18:13:31
18:13:32
18:13:33
18:13:34
task says good-bye!
task cancelled

You can also comment the “Thread.Sleep(5000)” out. The returned task state does not change.
Btw. the equivalent of “ThrowIfCancellationRequested()” is

  if (token.IsCancellationRequested) throw new OperationCanceledException(token);

And here is a quick example of using timeouts in case your tasks take longer than expected.

static void CancelTask4() {
    CancellationTokenSource lCancellationTokenSource = new CancellationTokenSource();
    CancellationToken t = lCancellationTokenSource.Token;

    Task lTask1 = Task.Factory.StartNew(() => ArbitraryTask1(t), t);
    Task lTask2 = Task.Factory.StartNew(() => ArbitraryTask1(t), t);

    int  i = Task.WaitAny(new Task[] { lTask1, lTask2 }, 5000);
    if (i == -1) {
        Console.WriteLine("tasks timed out");
        lCancellationTokenSource.Cancel();  // you can comment this line out to see the change in behaviour
        if (!Task.WaitAll(new Task[] { lTask1, lTask2 }, 5000)) {
            Console.WriteLine("OMG, something went wrong again!");
        }
    }

    Console.ReadLine();
} //

Introduction

Programming is not about knowing the programming language. You can learn any language in a few days. It is about algorithms. How do you solve a problem efficiently? All roads lead to Rome, but some are shorter or faster than others.
In this blog I will try building up a reference guide for hardcore programming. This includes techniques to solve problems in most efficient ways.
I aim at developers, who have some experience with C#, but want to deepen their knowledge and make sure they are ready to at least be able to pass the C# Microsoft Solution Developer (MCSD) 70-483 easily.

I am active in the stock market trading business since 1997. So I will finally go down that road and concentrate on speed. I wish I could explain everything in a single day, but realistically we are looking at roughly two years before I get there. My time is limited, this is my second blog and I know a good blog needs some dedication.

You should have at least one year of experience programming essential business logic.