/// <summary>
/// 长时间工作Task服务
/// </summary>
public class LongRunningTaskService
{
private CancellationTokenSource _stopCts;
private readonly SemaphoreSlim pauseSlim = new SemaphoreSlim(0, 1);
private Task _workTask;
public void Start()
{
_stopCts = new();
_workTask = Task.Factory.StartNew(async () => await TaskJob(_stopCts.Token), _stopCts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
if (pauseSlim.CurrentCount == 0)
pauseSlim.Release();
Console.WriteLine($"Starting Service...");
}
public void Pause()
{
Console.WriteLine("Pausing Service...");
pauseSlim.Wait();
}
public void Resume()
{
Console.WriteLine("Resuming Service...");
pauseSlim.Release();
}
public void Stop()
{
Console.WriteLine("Stopping Service...");
_stopCts?.Cancel();
_stopCts?.Dispose();
_stopCts = null;
_workTask = null;
}
private async Task TaskJob(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
if (pauseSlim.CurrentCount == 0)
{
await pauseSlim.WaitAsync(token);
pauseSlim.Release();
}
Console.WriteLine("Working...");
await Process(token);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Service Stopped");
}
}
/// <summary>
/// 模拟单个耗时异步任务
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
private async Task Process(CancellationToken token)
{
await Task.Delay(1000, token);
}
}
- 关键点解释
- 使用CancellationTokenSource来控制Task任务的取消(停止),将_stopCts作为线程委托启动参数传递(_stopCts调用Cancel方法时,异步任务的委托方法的while轮询将会跳出,从而达到取消任务效果)。
- 将长时间运行的Task标记为TaskCreationOptions.LongRunning的作用:该Task任务将不是默认从线程池中获取线程,而是单独创建一个独立线程来运行任务。
- 注意:CancellationTokenSource调用Cancel后,它的CancellationToken传递至的地方,都有可能触发OperationCanceledException或TaskCanceledException,记得捕获这两个异常。
- 适合异步方法中的信号量很少,这里使用SemaphoreSlim来实现控制Task任务的暂停和继续。通过调用Wait将该信号量的CurrentCount置为0,从而使Task委托进入“等待获取进入权限”状态,实现暂停效果。通过调用Release方法,使Task委托的pauseSlim.WaitAsync获取到“继续运行的权限”(WaitAsync成功后一定要调用Release方法释放锁,防止死锁,同时也将CurrentCount置为1),实现继续效果。