如何正确重置CancellationToken?

76

今天早上我一直在使用 Async CTP,有一个简单的程序,里面有一个button和一个label。点击 button 后,它就开始更新label,停止 button 后,它会停止写入到 label 中。但是,我不知道如何重置 CancellationTokenSource 以便重新启动该进程。

我的代码:

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();
        }
    }
}
3个回答

113
你需要重新创建一个 CancellationTokenSource,因为一旦设置了它就无法“重置”它。
这可能只需要简单地执行以下操作:
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();
    }
}

2
你应该在应用程序关闭时何时处理它?因为你必须等待线程完成,否则会出现ObjectDisposed异常。 - Zerowalker
@user2587718 这取决于对象的类型等等。我建议您就此提出自己的问题。 - Reed Copsey
在处理之前确实应该进行空值检查,但这也很好,因为这正是我正在做的事情。 - Louis Duran
试图学习更多关于CancellationTokens的知识,为什么你在Cancel调用后没有立即处理cts?它只有在下一次被新建时才应该被处理吗? - JKennedy

10

我遇到了同样的问题,后来发现解决它的最好方法是在调用该方法之前新创建一个取消令牌源。

这是我在启动按钮点击时所做的:

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

我将同一个按钮的标题更改为“取消”,当点击“取消”时,我调用

cancellationTokenSource.Cancel();

以下是完整代码:

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);
    }
}

6

现在,通过.NET 6你可以使用TryReset()方法,该方法尝试将CancellationTokenSource重置为用于不相关的操作。详情请参见问题以及CancellationTokenSource.cs


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接