我让DeepSeek帮我写了一段使用 CancellationTokenSource 取消任务的简单示例如下:
取消任务的简单示例
csharp
using System.Threading.Tasks;
using System.Threading;
public async Task Test()
{
var cts = new CancellationTokenSource();
var task = Task.Run(() =>
{
while (true)
{
cts.Token.ThrowIfCancellationRequested();
Thread.Sleep(200);
}
});
await Task.Delay(1000);
cts.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
// task is cancelled
}
finally
{
cts.Dispose();
}
}
相对于不可取消的普通任务多了很多代码,并且终止任务的方式 ThrowIfCancellationRequested
内部是通过抛出异常实现的
ThrowIfCancellationRequested 内部代码
csharp
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested)
{
throw new OperationCanceledExceptionEx(this);
}
}
于是我就想自己封装一个简单的包装类,代码如下:
CancelableTask
csharp
namespace System.Threading.Tasks
{
public sealed class CancelableTask : Task
{
readonly object cancelLock = new object();
CancellationTokenSource cts;
public bool IsCancellationRequested => cts != null && cts.IsCancellationRequested;
private CancelableTask(Action action, CancellationToken token) : base(action, token) { }
protected override void Dispose(bool disposing)
{
if (disposing)
{
cts?.Dispose();
}
base.Dispose(disposing);
}
public void Cancel()
{
lock (cancelLock)
{
if (!IsCompleted && !IsCancellationRequested)
{
cts.Cancel();
}
}
}
public static CancelableTask StartNew(Action action)
{
var cts = new CancellationTokenSource();
var task = new CancelableTask(action, cts.Token);
task.cts = cts;
task.Start();
return task;
}
public static void Restart(ref CancelableTask task, Action action)
{
var oldTask = Interlocked.Exchange(ref task, null);
oldTask?.Cancel();
var newTask = StartNew(action);
Interlocked.Exchange(ref task, newTask);
}
}
}
此类公开了 Cancel
用来取消任务,静态的 StartNew
用来创建新实例,以及静态的 Restart
用来取消上个任务并重启任务,
还公开了一个 IsCancellationRequested
属性用来获取是否请求过取消任务,相对于调用 ThrowIfCancellationRequested
抛出异常来终止任务的方式性能更好也更加友好
使用此类重新编写上面的示例就简单很多了
CancelableTask 使用示例
csharp
using System.Threading.Tasks;
CancelableTask task;
public async Task Test()
{
task = CancelableTask.StartNew(() =>
{
while (!task.IsCancellationRequested)
{
Thread.Sleep(200);
}
});
await Task.Delay(1000);
task.Cancel();
await task;
}