1

Is there a way to fetch currently running task by ID once you get out of the method where you initially started the task? I would like to get somehow current tasks so I can stop some of them mid-execution without interrupting the flow of the program.

For example:

class Test
{
    List<Task> listOfTasks = new List<Task>();


    void taskCaller()
    {
        try
        {
            var myTask = Task.Factory.StartNew(()=> testMethod());
            listOfTasks.Add(myTask);
        }
        catch (System.Exception)
        {
            //...
            throw;
        }
    }


    void taskGetter()
    {
        foreach (var item in listOfTasks)
        {
            if(item == something)
            {
                item.KillExecution();
            }
        }
    }


    private void testMethod()
    {
        // doing something
        throw new NotImplementedException();
    }
}

EDIT

I want it done without cancellation tokens if possible as it doesn't work for me as I want it to because I am running a timer fetching DLL from DB and cancellation tokens work only if I immediately kill the task with token.Cancel() however if I try token.CancelAfter() it doesn't work because it gets triggered only after the task finished with the execution, which kind of isn't the idea.

EDIT2:

Not to copy tons of code, here is the pseudo of the whole flow:

new timer(repeat method every xx time units)
    open DB
    do some work
    save DB

Continuing with plast1k suggestion...

When I check the dictionary if the cancellation is requested in this scenario, it says true but it doesn't do anything to the running task

private void getActiveTasks()
{
    if (!ListOfTasks.Any())
        return;
    Console.WriteLine("Currently running:");
    foreach (var task in ListOfTasks)
    {
        Console.WriteLine("\t" + task.Key);
        Console.WriteLine("\t" + task.Value.MyTask.Id);
        task.Value.Token.Cancel();
        Console.WriteLine("\t" + task.Value.Token.IsCancellationRequested);


        ListOfTasks.Remove(task.Key);
        return;
    }
}
Community
  • 1
  • 1
Norgul
  • 4,613
  • 13
  • 61
  • 144
  • 1
    Not trying to be a jerk but if cancellation tokens aren't working then you're doing something wrong. Maybe you could explain why they don't work? See [the XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – Equalsk Oct 27 '16 at 15:15
  • Here, I've updated it – Norgul Oct 27 '16 at 15:20
  • It still sounds like you're implementing cancellation tokens incorrectly. It's a cooperative effort, the method that fetches from the DB should be regularly checking if the passed in token has been cancelled and if it has the method should exit gracefully. Not sure why you'd use CancelAfter over Cancel, especially if you want to cancel as soon as possible? – Equalsk Oct 27 '16 at 15:29
  • I don't...I want it to terminate after some timeout – Norgul Oct 27 '16 at 15:30
  • 1
    Makes no difference, when you ask to cancel the method it is either already fetching from the database or it isn't, if you're checking for cancellation tokens before and after this it will still exit gracefully. If you're asking to forcibly bomb out of the method then that's a bad idea, there's a reason the tokens are implemented this way. – Equalsk Oct 27 '16 at 15:35

3 Answers3

2

As you might have already researched, by far the only way to cancel a task is to make it aware of the cancellation using the cancellationtoken and making it cancellation-aware. Also, that is the preferred way to do it as well.However, as your question mentions that you can't use a cancellation source you may use the technique mentioned in another SO answer (link included).

A gist from the link, You may create those tasks and run them on a different thread and abort the thread whenever you may want to cancel the task

How do I force a Task to stop?

Community
  • 1
  • 1
usaipavan
  • 103
  • 7
1

Make sure you are actually handling cancellation inside your task (via cancelToken.ThrowIfCancellationRequested() ) as calling Cancel on the token source only cancels the task scheduling, it does not actually stop execution.

The proper way to stop a task would be to use cancellation tokens. See an example here

https://msdn.microsoft.com/en-us/library/dd997396(v=vs.110).aspx

and here

https://blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/recommended-patterns-for-cancellationtoken/

brakeroo
  • 1,407
  • 13
  • 24
  • By his question it seems the method that he is trying to run does not support cancellation tokens, so even if the wrapper method did he couldn't cancel the external call. – plast1k Oct 27 '16 at 15:25
1

While you should see usaipavan's answer and link for proper cancellation technique, in order to keep track of your threads or tasks you could simply add them to a Dictionary instead of a List and assign the ID of your choosing. It could be an integer or a string or anything, in this example its a GUID.

Dictionary<Guid, Task> listofTasks = new Dictionary<Guid, Task>();

public Guid StartTask()
{
    Task myTask = Task.Factory.StartNew(()=> testMethod());
    Guid taskID = Guid.NewGuid();
    listOfTasks.Add(taskID, myTask);
    return taskID;
}

public void CancelTask(Guid id)
{
    // listOfTasks[id].choose_your_own_task_cancellation_adventure()
}

If you could use cancellation tokens you could store those in the dictionary/another dictionary/as a tuple. If you wanted to identify groups of these tasks (i.e., cancel all of the asks for the "DB Query" operation) you could do something like this

public class TaskWrapper
{
    public TaskWrapper(string operationID, Task task, CancellationTokenSource cancellationSource)
    {
        this.OperationID = operationID;
        this.Task = task;
        this.CancellationSource = cancellationSource;
    }
    public string OperationID { get; set; }
    public Task Task { get; set; }
    public CancellationTokenSource CancellationSource { get; set; }
}


private Dictionary<Guid, TaskWrapper> ListOfTasks = new Dictionary<Guid, TaskWrapper>();


// populate the list


public void CancelAll(string operation)
{
    listOfTasks.Where(a => a.OperationID == operation).CancellationSource.Cancel();
}
plast1k
  • 793
  • 8
  • 15
  • Idea is amazing, but I can't seem to get tokens to work...since I'm using a timer (look edit), is it possible that token that is related to specific task is somehow "overriden" and is no longer related to it, so if I cancel it...nothing happens? – Norgul Oct 28 '16 at 08:40
  • Is something like `MyTask[id].Token.Cancel()` possible? So I directly know to which task the token is being forwarded? – Norgul Oct 28 '16 at 09:06
  • @Norgul Yes thats possible - See https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx for more information on the dictionary. Also you are right that you may not be able to use a cancellation token to properly cancel the scenario you've provided. – plast1k Oct 28 '16 at 12:18
  • I know how to use Dictionary...do you know a link to see how to fetch task by id or some other identifier? Or access its `CancellationRequested` property directly? – Norgul Oct 28 '16 at 12:25