76

I have been playing round with the Async CTP this morning and have a simple program with a button and a label. Click the button and it starts updating the label, stop the button it stops writing to the label. However, I'm not sure how to reset the CancellationTokenSource so that I can restart the process.

My code:

public partial class MainWindow : Window
{
    CancellationTokenSource cts = new CancellationTokenSource();
    public MainWindow()
    {
        InitializeComponent();
        button.Content = "Start";
    }

    async Task DoWork(CancellationToken cancelToken)
    {
        int i = 0;
        while (!cancelToken.IsCancellationRequested)
        {
            label.Content = i++.ToString();
            await TaskEx.Delay(50, cancelToken);
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (button.Content == "Start")
        {
            button.Content = "Stop";
            DoWork(cts.Token);
        }
        else
        {
            button.Content = "Start";
            cts.Cancel();
        }
    }
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
poco
  • 2,935
  • 6
  • 37
  • 54

3 Answers3

112

You need to recreate the CancellationTokenSource - there is no way to "reset" this once you set it.

This could be as simple as:

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (button.Content == "Start")
    {
        button.Content = "Stop";
        cts.Dispose(); // Clean up old token source....
        cts = new CancellationTokenSource(); // "Reset" the cancellation token source...
        DoWork(cts.Token);
    }
    else
    {
        button.Content = "Start";
        cts.Cancel();
    }
}
cdbitesky
  • 1,390
  • 1
  • 13
  • 30
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 2
    When are you supposed to dispose of it on close of the application? As you have to wait before the Thread is done, else you get ObjectDisposed exception. – Zerowalker Aug 25 '13 at 09:01
  • @user2587718 It really depends on what type of object, etc. I'd recommend asking your own question regarding this. – Reed Copsey Aug 26 '13 at 16:47
  • Should really do a null check before disposing but this was good since it is exactly what I am doing. – Louis Duran Apr 20 '18 at 21:33
  • Trying to learn abit more about CancellationTokens, Why are you not disposing of the cts straight after the `Cancel` call? Should it only be disposed of the next time it is about to be newed up? – JKennedy Dec 06 '19 at 10:24
10

I had the same problem and I figured it out that the best way to solve it is to create cancellation token source newly just before calling the method.

this is what I do on my start button click:

cancellationTokenSource = new CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;
Task.Factory.StartNew(StartUpload, cancellationToken);

I change the caption for the same button to cancel and when a click occurs on cancel, I call

cancellationTokenSource.Cancel();

Here is the full code:

if (button3.Text != "&Start Upload")
{
    cancellationTokenSource.Cancel();
}
else
{
    try
    {
        cancellationTokenSource = new CancellationTokenSource();
        cancellationToken = cancellationTokenSource.Token;
        Task.Factory.StartNew(StartUpload, cancellationToken);
    }
    catch (AggregateException ex)
    {
        var builder = new StringBuilder();
        foreach (var v in ex.InnerExceptions)
            builder.Append("\r\n" + v.InnerException);
        MessageBox.Show("There was an exception:\r\n" + builder.ToString());
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
Amit
  • 25,106
  • 25
  • 75
  • 116
6

Now, with .NET 6 you can use TryReset() method which Attempts to reset the CancellationTokenSource to be used for an unrelated operation. see this issue and CancellationTokenSource.cs for more details.