-1

I have an WPF application in which I am generating numbers of task for parallel processing. Now when my condition becomes true then I need to cancel particular that task. When I am applying CancellationTokenSource for cancel task then it's cancel whole bunch of task but I need to cancel only single.

Task t1 = Task.Run(() => Task.Delay(30000, token), token);
                test.Add(t1.Id, symbolName);
                lstTask.Add(t1);
                await t1;

here I am holding Task Id so I can know that which Task should I cancel ?

So please can you suggest me that how can I able to cancel only single Task by it's Id ?

After discussion about problem I have made some changes in code so please suggest me that should I change my logic ? because recently I aware that task Id is not unique so I prefer CTS directly.

public static async void startAddToPosition(System.Threading.CancellationTokenSource cts1, string symbolName)
{
          try
          {
               Task t1 = Task.Run(() => Task.Delay(30000, cts1.Token), cts1.Token);
                test.Add(symbolName, cts1);
                lstTask.Add(t1);
                lstCts.Add(cts1);
                await t1;
                generateRealPosition("QQQ"); //here I need symbol name when task will complete it's 30 seconds.
          }
          catch (OperationCanceledException ex)
          {
                var symbolName = test.FirstOrDefault(q => q.Value.Token == ex.CancellationToken).Key;
            }
}

however on the other side when condition become full fill then I am trying to cancel task like below :

CancellationTokenSource cts = Evaluation.parallelScripts.test.FirstOrDefault(q => q.Key == eventData.symbolName).Value;
  cts.Cancel();

So I am trying to get CancellationTokenSource for particular symbol from list and cancelling that CTS. So that task become cancel.

So my question is that I don't know that CTS token is unique or not. because when CTS cancelling Task then in exception I need Symbol Name according to that CTS token.

  • 2
    Youll need more than one cancellation token i guess. Keep them in a `Dictionary` with the Task Id.. however Task Id seems a little brittle (though it should be ok) – TheGeneral May 14 '19 at 09:49
  • Your sample code in the question seems rather wrong.What's the `test.Add` about? Where is `symbolName` defined? Why are you awaiting `t1`? – Enigmativity May 14 '19 at 10:24
  • @Enigmativity, Actually I am getting symbol from live stock market very speedy which I am handling using parallel processing and after process I need to send those symbols list to clients for suggestion. So right now I am assigning a task to every symbol and then I am waiting for response for that symbol from clients and when I am getting response then I need to cancel particular that task which I have assign to that symbol. In test.Add I am storing task Id with symbol name so I can cancel particular that task and symbol which I get from client side as a response. – Bhavesh Ghul May 14 '19 at 10:31
  • @BhaveshGhul - It sounds like you really need to be using Microsoft's Reactive Extensions for this. It'd be only a handful of lines. Any chance you could show me a [mcve]? – Enigmativity May 14 '19 at 10:36
  • You can't cancel a task by its `Id`. You need the `CancellationToken` that was passed at the constructor of the task. – Theodor Zoulias May 14 '19 at 11:37
  • @TheodorZoulias, token is a CancellationToken which I am applying with task. So token become cancel then it's cancelling whole bunch of task but I need to cancel particular task. I am storing task Id and identification Name in dictionary. So I need to cancel task by it's id. – Bhavesh Ghul May 14 '19 at 12:06
  • If you want to cancel specific tasks, then the logical thing would be to supply a different `CancellationToken` to each task. You may not **want** to, but you may **have** to because there is no other choice. – Theodor Zoulias May 14 '19 at 12:23

2 Answers2

1

Each cancellation token source is a separate logical "cancel". As you've found, if you share cancellation tokens, then a that single "cancel" will cancel all of them.

To create multiple logical "cancel"s, you'll need multiple cancellation token sources:

CancellationTokenSource cts1 = new CancellationTokenSource();
Task t1 = Task.Run(() => Task.Delay(30000, cts1.Token), cts1.Token);
test.Add(t1.Id, symbolName, cts1);
lstTask.Add(t1);
lstCts.Add(cts1);
await t1;

can you suggest me that how can I able to cancel only single Task by it's Id ?

This is not possible.

For one, it's not possible to cancel a task from the "outside". All you can do is give it a token and cancel that token. It's up to the task to respond to that token appropriately. This is how cooperative cancellation works.

There's another reason this isn't possible: task ids are not unique. It appears you are using task ids in your code; I recommend you re-evaluate the design to ensure that it can handle non-unique task ids. Perhaps using the actual Task instance instead (which is unique).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Thank you for your suggestions but I was not aware that task ids are unique. So I was trying to cancel task using task id. here test.Add contains cts1 in suggested code. So will it be unique on every task ? – Bhavesh Ghul May 14 '19 at 13:50
  • 1
    Both the `CancellationTokenSource` and `Task` instances will be unique. Task ids are not unique. – Stephen Cleary May 14 '19 at 13:51
  • Actually I am a bit confuse with this like if I need to cancel a Task which is assign to AAPL. so how could I cancel that particular task which I have assigned to AAPL ? – Bhavesh Ghul May 14 '19 at 14:01
  • @BhaveshGhul: You would cancel the corresponding `CancellationTokenSource` - the one that was created at the same time the task was created. – Stephen Cleary May 14 '19 at 14:33
  • As per your suggestion I have change code in that I need to use token of CancellationTokenSource. So is it unique or not ? because I need to fetch Symbol Name from cancellationTokenSource.Token. – Bhavesh Ghul May 14 '19 at 14:39
  • Each `CancellationTokenSource` instance is unique. Tokens are equivalent if they refer to the same `CancellationTokenSource`. – Stephen Cleary May 14 '19 at 14:54
0

You can do something like this:

class Program
{
    static void Main(string[] args)
    {
        Guid lastID = default(Guid);
        List<CancellableTask> cancellableTasks = new List<CancellableTask>();
        for (int i = 0; i < 10; i++)
        {
            CancellableTask task = new CancellableTask(() =>
            {
                Console.WriteLine("New task!");
                Thread.Sleep(3000);
            });
            cancellableTasks.Add(task);
            lastID = task.ID;
        }

        CancellableTask cancellableTask = cancellableTasks.FirstOrDefault(x => x.ID == lastID);
        if (cancellableTask != null)
        {
            cancellableTask.Cancel();
        }
    }
}

public class CancellableTask
{
    public Guid ID { get; private set; }
    private CancellationTokenSource CancellationTokenSource { get; set; }
    public Task Task { get; private set; }
    public CancellableTask(Action action)
    {
        CancellationTokenSource = new CancellationTokenSource();
        Task = Task.Run(action, CancellationTokenSource.Token);
        ID = Guid.NewGuid();
    }
    public void Cancel()
    {
        CancellationTokenSource.Cancel();
    }
}
Marco Salerno
  • 5,131
  • 2
  • 12
  • 32
  • I want to make it async await for 30 seconds. Please can you suggest me that how can I convert it to async await and wait for 30 seconds ? – Bhavesh Ghul May 14 '19 at 10:23
  • @BhaveshGhul - What do you mean? – Enigmativity May 14 '19 at 10:28
  • @Enigmativity, I mean when I am trying above code in implementation and cancel task with using CTS then also it's waiting for 30 seconds while my concept is when task become cancel then it should not wait for remaining seconds. In my existing code I am getting Exception when task become cancel. but it's not happen in this scenario. – Bhavesh Ghul May 14 '19 at 12:02