Click here to Skip to main content
15,884,986 members
Articles / Programming Languages / C#

How to control parallel thread execution without passing CancellationTokenSource or similar objects into your background code

Rate me:
Please Sign up or sign in to vote.
4.45/5 (8 votes)
21 Feb 2013CPOL2 min read 21.6K   320   11   5
This article describes a tiny class that allows to pass a cancellation flag to your background code, without modifying your existing interfaces and function prototypes.

Introduction

It's a normal situation when you need to control execution of parallel threads but don't want (or can't) pass additional arguments like CancellationTokenSource or similar objects into your code. This article describes a simple solution that allows to associate a cancellation token like object with a thread.

Background

For example let's imagine that you have some IAlgorithm interface like this one:

C#
public interface IAlgorithm
{
    void PrepareData(Func<object> func);
}

and you need some ability to stop execution of your function that you are passing as argument to PrepareData:

C#
public object MyFunctionToPrepareData()
{
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine("Step {0}", i);
        Thread.Sleep(1000);
    }

    return "Data for my algorithm.";
}

IAlgorithm algorithm = new MyAlgorithm();
algorithm.PrepareData(MyFunctionToPrepareData);

To pass CancellationTokenSource into MyFunctionToPrepareData you, obviously, have to change IAlgorithm and add one more parameter to the MyFunctionToPrepareData function. That is not always possible. It'll look like this:

C#
public interface IAlgorithm
{
    void PrepareData(Func<CancellationTokenSource, object> func);
}

public static object MyFunctionToPrepareData(CancellationTokenSource src)
{
    for (int i = 0; i < 10; i++)
    {
        if (src.IsCancellationRequested)
            Thread.CurrentThread.Abort();
        Console.WriteLine("Step {0}", i);
        Thread.Sleep(1000);
    }

    return "Data for my algorithm";
}

IAlgorithm algorithm = new MyAlgorithm();
CancellationTokenSource tokenSource = new CancellationTokenSource();
algorithm.PrepareData(tokenSource, MyFunctionToPrepareData);

In such a situation it can be very convenient to associate a cancellation flag with the current thread. You can do this with a tiny class described in this article.

Using the Code

The following class solves the described problem by binding thread cancellation info to the execution thread.

C#
public class CancellationScope: IDisposable
{
    [ThreadStatic] // one instance per thread 
    private static List<CancellationScope> _threadActiveScopes;

    public CancellationScope() 
    {
        ThreadActiveScopes.Add(this);
    }

    public bool IsCancellationRequested { get; private set; }

    public void Cancel()
    {
        IsCancellationRequested = true;
    }

    public static void TryContinue()
    {
        foreach (CancellationScope scope in ThreadActiveScopes)
        {
            if (scope.IsCancellationRequested)
                Thread.CurrentThread.Abort(); // throw ThreadAbortException
        }
    }

    private static List<CancellationScope> ThreadActiveScopes
    {
        get
        {
            if (_threadActiveScopes == null)
                _threadActiveScopes = new List<CancellationScope>();
            return _threadActiveScopes;
        } 
    }

    public void Dispose()
    {
        ThreadActiveScopes.Remove(this);
    }
}

As you can see this class allows to associate one or more CancellationScope instances with the current thread. Association achieved with the help of ThreadStatic attribute that guarantees that every thread will have own instance of CancellationScope list. So now everything that you need to check if the cancellation has been requested by the main flow is to call the CancellationScope.TryContinue() static function at some checkpoints. It'll get all CancellationScope instances associated with the current thread, and check the IsCancellationRequested property of these instances. To cancel a background task from your foreground code it's enough to call the myCurrentScope.Cancel() method.

Now the MyFunctionToPrepareData function supports the cancellation without any additional arguments:

C#
public object MyFunctionToPrepareData()
{
    for (int i = 0; i < 10; i++)
    {
        CancellationScope.TryContinue();
        Console.WriteLine("Step {0}", i);
        Thread.Sleep(1000);
    }

    return "Data for my algorithm";
}

And my background code:

C#
IAlgorithm algorithm = new MyAlgorithm();
using (CancellationScope scope = new CancellationScope())
{
    algorithm.PrepareData(MyFunctionToPrepareData);
    // pass scope variable to your foreground thread to allow to cancel execution
}

See all source code and usage example in attachments.

Points of Interest

You can also extend CancellationScope with some event (and TryContinue with a state object argument) to pass current execution progress to the foreground thread.

Please also take into account that CancellationScope should be created and destroyed only in the thread that you want to control.

History

  • Version 1 - 9 Feb 2013 - First version.
  • Version 2 - 17 Feb 2013 - Dictionary replaced with ThreadStatic attribute. It allows to simplify code, and get rid of lock statements.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Ukraine Ukraine
Software Architect - Net. C#, JavaScript
Web Site: http://icocentre.com/

Comments and Discussions

 
GeneralSmart Pin
Omar Gameel Salem7-Jan-14 1:13
professionalOmar Gameel Salem7-Jan-14 1:13 
QuestionNice trick Pin
mrchief_200022-Feb-13 7:39
mrchief_200022-Feb-13 7:39 
Question+5 Pin
RAND 45586621-Feb-13 10:04
RAND 45586621-Feb-13 10:04 
GeneralMy vote of 1 Pin
William E. Kempf11-Feb-13 11:03
William E. Kempf11-Feb-13 11:03 
GeneralSmall inefficiency spotted Pin
abdurahman ibn hattab11-Feb-13 10:50
abdurahman ibn hattab11-Feb-13 10:50 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.