从 .NET Framework 4 开始,Microsoft 提供了一个统一的模型来取消异步或长时间运行的同步操作:
取消令牌源 实现取消的一般模式是:
创建 CancellationTokenSource 对象 将新创建的 CancellationTokenSource 对象的 Token 属性传递给异步任务 在异步任务中,定期检查 CancellationToken.IsCancellationRequested 属性的值。如果为 true,则表示用户已取消操作。您需要正常结束操作。 在主线程中,可以通过调用 CancellationTokenSource.Cancel() 方法取消异步任务。这会将 CancellationToken.IsCancellationRequested 属性设置为 true。 代码示例 以下代码片段演示了如何使用 CancellationTokenSource。该窗体有两个按钮:“开始”和“取消”。单击“开始”按钮将初始化一个长时间运行的任务。单击“取消”按钮将触发 CancellationTokenSource 的 Cancel() 方法调用,并将 IsCancellationRequested 属性设置为 true。长时间运行的任务每秒检查一次此值,并在其值为 true 时终止。
namespace WinFormsApp1
{
public partial class Form1 : Form
{
private CancellationTokenSource ctsDemo;
static Form1()
{
}
public Form1()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
ctsDemo = new CancellationTokenSource();
try
{
LongRunningTask(ctsDemo.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
}
private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}
private void LongRunningTask(CancellationToken token)
{
Cursor = Cursors.WaitCursor;
Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
{
Log("Task running\r\n");
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log("Task cancelled\r\n");
}, token);
}
private void btnCancel_Click(object sender, EventArgs e)
{
Cursor = Cursors.Default;
ctsDemo.Cancel();
ctsDemo.Dispose();
}
}
}
自动将 IsCancellationRequested 设置为 true 通过调用 CancellationTokenSource.CancelAfter(
代码示例 以下代码片段将取消时间设置为 5 秒。5 秒后,框架会将令牌的 IsCancellationRequested 属性设置为 true,因此任务将结束。
namespace WinFormsApp1
{
public partial class Form2 : Form
{
private CancellationTokenSource ctsDemo;
public Form2()
{
InitializeComponent();
}
private async void btnStart_Click(object sender, EventArgs e)
{
ctsDemo = new CancellationTokenSource();
ctsDemo.CancelAfter(TimeSpan.FromSeconds(5));
try
{
Cursor = Cursors.WaitCursor;
await LongRunningTask(ctsDemo.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
finally { Cursor = Cursors.Default; }
}
private Task LongRunningTask(CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
int count = 0;
while (!token.IsCancellationRequested)
{
count++;
Log($"Task {Task.CurrentId} running {count}\r\n");
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log("Task cancelled\r\n");
}, token);
}
private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}
}
}
链接取消令牌源 您可以使用 CancellationTokenSource.CreateLinkedTokenSource(params CancellationToken[] tokens) 方法创建链接的取消令牌源。当任何源令牌处于“已取消”状态时,新创建的取消令牌源将处于“已取消”状态。如果您有多个任务正在运行,这将非常有用。其中一个任务依赖于其他任务。如果任何其他任务失败或取消,则不希望可靠任务继续执行。在这种情况下,您可以使用链接的取消令牌源来自动取消可靠的任务。
代码示例 在下面的代码片段中,我们创建了一个主要的取消令牌源,并使用 CreateLinkedTokenSource() 方法创建了一个链接的取消令牌源。
当主取消令牌源被取消时,链接的源也会自动取消。但是,如果只是取消链接的令牌源,则主令牌源不受影响。
namespace WinFormsApp1
{
public partial class Form3 : Form
{
private CancellationTokenSource ctsMain;
private CancellationTokenSource ctsLinked;
public Form3()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
ctsMain = new CancellationTokenSource();
ctsLinked = CancellationTokenSource.CreateLinkedTokenSource(ctsMain.Token);
try
{
LongRunningTask(ctsMain.Token);
LongRunningTask(ctsLinked.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
}
private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}
private Task LongRunningTask(CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
{
Log($"Task {Task.CurrentId} running\r\n");
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log($"Task {Task.CurrentId} cancelled\r\n");
}, token);
}
private void btnCancel1_Click(object sender, EventArgs e)
{
ctsMain.Cancel();
}
private void btnCancel2_Click(object sender, EventArgs e)
{
ctsLinked.Cancel();
}
}
}