如何捕获 Task.Delay() 取消并进行回调?
How to catch that Task.Delay() canceled and do a callback?
假设我有一个工人 class。
public sealed class Worker : IDisposable
{
private bool _isRunning;
private CancellationTokenSource _cts;
private readonly Action _action;
private readonly int _millisecondsDelay;
private readonly object _lock = new object();
public Worker(Action action, int millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay = 5000;
}
public void Start()
{
lock (_lock)
{
if (!_isRunning)
{
_isRunning = true;
Run();
}
}
}
public void Cancel()
{
lock (_lock)
{
if (_isRunning) _cts.Cancel();
}
}
private void Run()
{
using (_cts) _cts = new CancellationTokenSource();
Task.Run(async () => { await DoAsync(_cts.Token); });
}
private async Task DoAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
//this code is unreachable
lock (_lock)
{
_isRunning = false;
}
}
public void Dispose()
{
try
{
_cts?.Cancel();
}
finally
{
if (_cts != null)
{
_cts.Dispose();
_cts = null;
}
}
}
}
问题是代码 _isRunning = false;
无法访问。我的意思是更有可能当调用者调用 Cancel
方法时工作人员将等待 Task.Delay。那么在我的任务被取消后我如何调用 smth(这里是 _isRunning = false;
)?换句话说,我需要确保我的工人不是 运行(它不是取消状态)
要回答您的字面问题,您可以使用 finally
块:
private async Task DoAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
但我对这种 "worker" 方法有些担忧:
- 我不太喜欢里面的即发即弃式
Run
。我怀疑你会想要改变它。
- 将
lock
与异步代码混合使用可能会出现问题。你应该绝对确定这是你真正想做的。
可能值得退后一步,重新考虑您实际想要使用此代码做什么。
假设我有一个工人 class。
public sealed class Worker : IDisposable
{
private bool _isRunning;
private CancellationTokenSource _cts;
private readonly Action _action;
private readonly int _millisecondsDelay;
private readonly object _lock = new object();
public Worker(Action action, int millisecondsDelay)
{
_action = action;
_millisecondsDelay = millisecondsDelay = 5000;
}
public void Start()
{
lock (_lock)
{
if (!_isRunning)
{
_isRunning = true;
Run();
}
}
}
public void Cancel()
{
lock (_lock)
{
if (_isRunning) _cts.Cancel();
}
}
private void Run()
{
using (_cts) _cts = new CancellationTokenSource();
Task.Run(async () => { await DoAsync(_cts.Token); });
}
private async Task DoAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
//this code is unreachable
lock (_lock)
{
_isRunning = false;
}
}
public void Dispose()
{
try
{
_cts?.Cancel();
}
finally
{
if (_cts != null)
{
_cts.Dispose();
_cts = null;
}
}
}
}
问题是代码 _isRunning = false;
无法访问。我的意思是更有可能当调用者调用 Cancel
方法时工作人员将等待 Task.Delay。那么在我的任务被取消后我如何调用 smth(这里是 _isRunning = false;
)?换句话说,我需要确保我的工人不是 运行(它不是取消状态)
要回答您的字面问题,您可以使用 finally
块:
private async Task DoAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
//Log.Message1("____REFRESHING STATUS____");
_action();
await Task.Delay(_millisecondsDelay, cancellationToken);
}
}
finally
{
lock (_lock)
{
_isRunning = false;
}
}
}
但我对这种 "worker" 方法有些担忧:
- 我不太喜欢里面的即发即弃式
Run
。我怀疑你会想要改变它。 - 将
lock
与异步代码混合使用可能会出现问题。你应该绝对确定这是你真正想做的。
可能值得退后一步,重新考虑您实际想要使用此代码做什么。