CancellationToken.ThrowIfCancellationRequested 不扔
CancellationToken.ThrowIfCancellationRequested not throwing
我编写了一个非常简单的应用程序来实现一些基于任务的异步操作。
客户端代码调用一个 returns 任务的方法。我将 CancellationToken 传递给该方法,但即使我在此过程中调用 CancellationToken.ThrowIfCancellationRequested 方法,取消也不会抛出 OperationCancelledException。
如果你想自己测试,你可以在这里下载整个解决方案:
https://github.com/stevehemond/asynctap-example
这是代码:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsyncTapExample
{
public partial class MainForm : Form
{
private const int totalSeconds = 5;
private bool isStarted;
public MainForm()
{
this.InitializeComponent();
}
private async void processingButton_Click(object sender, EventArgs e)
{
var cts = new CancellationTokenSource();
if (!this.isStarted)
{
this.processingButton.Text = "Cancel";
this.isStarted = true;
var progressIndicator = new Progress<int>(this.ReportProgress);
try
{
await this.ProcessLongRunningOperationAsync(progressIndicator, cts.Token);
MessageBox.Show("Completed!");
}
catch (OperationCanceledException)
{
MessageBox.Show("Cancelled!");
}
this.ResetUI();
}
else
{
cts.Cancel();
this.processingButton.Text = "Start";
this.isStarted = false;
}
}
private void ResetUI()
{
this.progressBar.Value = 0;
this.processingButton.Enabled = true;
this.progressMessageLabel.Text = string.Empty;
this.isStarted = false;
this.processingButton.Text = "Start";
}
private Task ProcessLongRunningOperationAsync(IProgress<int> progress, CancellationToken ct)
{
return Task.Run(
() =>
{
for (var i = 0; i <= totalSeconds; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
progress?.Report((i * 100) / totalSeconds);
}
},
ct);
}
private void ReportProgress(int progressPercentage)
{
this.progressBar.Value = progressPercentage;
this.progressMessageLabel.Text = $"{progressPercentage}%";
}
}
}
关于将 CancellationToken 传递给 Tasks,肯定有一些我不明白的地方……我只是想不通是什么。
您在每次调用 processingButton_Click
时创建 CancellationTokenSource
。结果,您取消了与用于创建任务的内容不同的 CancellationTokenSource
。你应该只在创建新任务时创建新的CancellationTokenSource
,并且你应该保存那个CancellationTokenSource
,这样你就可以取消它:
private CancellationTokenSource cts; //MainForm instance field
private async void processingButton_Click(object sender, EventArgs e)
{
if (!this.isStarted)
{
this.cts = new CancellationTokenSource();
this.processingButton.Text = "Cancel";
this.isStarted = true;
var progressIndicator = new Progress<int>(this.ReportProgress);
try
{
await this.ProcessLongRunningOperationAsync(progressIndicator, this.cts.Token);
MessageBox.Show("Completed!");
}
catch (OperationCanceledException)
{
MessageBox.Show("Cancelled!");
}
this.ResetUI();
}
else
{
this.cts.Cancel();
this.processingButton.Text = "Start";
this.isStarted = false;
}
}
我编写了一个非常简单的应用程序来实现一些基于任务的异步操作。
客户端代码调用一个 returns 任务的方法。我将 CancellationToken 传递给该方法,但即使我在此过程中调用 CancellationToken.ThrowIfCancellationRequested 方法,取消也不会抛出 OperationCancelledException。
如果你想自己测试,你可以在这里下载整个解决方案: https://github.com/stevehemond/asynctap-example
这是代码:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsyncTapExample
{
public partial class MainForm : Form
{
private const int totalSeconds = 5;
private bool isStarted;
public MainForm()
{
this.InitializeComponent();
}
private async void processingButton_Click(object sender, EventArgs e)
{
var cts = new CancellationTokenSource();
if (!this.isStarted)
{
this.processingButton.Text = "Cancel";
this.isStarted = true;
var progressIndicator = new Progress<int>(this.ReportProgress);
try
{
await this.ProcessLongRunningOperationAsync(progressIndicator, cts.Token);
MessageBox.Show("Completed!");
}
catch (OperationCanceledException)
{
MessageBox.Show("Cancelled!");
}
this.ResetUI();
}
else
{
cts.Cancel();
this.processingButton.Text = "Start";
this.isStarted = false;
}
}
private void ResetUI()
{
this.progressBar.Value = 0;
this.processingButton.Enabled = true;
this.progressMessageLabel.Text = string.Empty;
this.isStarted = false;
this.processingButton.Text = "Start";
}
private Task ProcessLongRunningOperationAsync(IProgress<int> progress, CancellationToken ct)
{
return Task.Run(
() =>
{
for (var i = 0; i <= totalSeconds; i++)
{
ct.ThrowIfCancellationRequested();
Thread.Sleep(1000);
progress?.Report((i * 100) / totalSeconds);
}
},
ct);
}
private void ReportProgress(int progressPercentage)
{
this.progressBar.Value = progressPercentage;
this.progressMessageLabel.Text = $"{progressPercentage}%";
}
}
}
关于将 CancellationToken 传递给 Tasks,肯定有一些我不明白的地方……我只是想不通是什么。
您在每次调用 processingButton_Click
时创建 CancellationTokenSource
。结果,您取消了与用于创建任务的内容不同的 CancellationTokenSource
。你应该只在创建新任务时创建新的CancellationTokenSource
,并且你应该保存那个CancellationTokenSource
,这样你就可以取消它:
private CancellationTokenSource cts; //MainForm instance field
private async void processingButton_Click(object sender, EventArgs e)
{
if (!this.isStarted)
{
this.cts = new CancellationTokenSource();
this.processingButton.Text = "Cancel";
this.isStarted = true;
var progressIndicator = new Progress<int>(this.ReportProgress);
try
{
await this.ProcessLongRunningOperationAsync(progressIndicator, this.cts.Token);
MessageBox.Show("Completed!");
}
catch (OperationCanceledException)
{
MessageBox.Show("Cancelled!");
}
this.ResetUI();
}
else
{
this.cts.Cancel();
this.processingButton.Text = "Start";
this.isStarted = false;
}
}