У меня есть метод RestartAsync
, который запускает метод DoSomethingAsync
. Когда RestartAsync
вызывается снова, он должен отменить DoSomethingAsync
и ждать, пока он не будет завершен (DoSomethingAsync
НЕ может быть отменен синхронно, и его НЕ следует вызывать, когда предыдущая задача еще выполняется).
Мой первый подход выглядел так:
public async Task RestartTest()
{
Task[] allTasks = { RestartAsync(), RestartAsync(), RestartAsync() } ;
await Task.WhenAll(allTasks);
}
private async Task RestartAsync()
{
_cts.Cancel();
_cts = new CancellationTokenSource();
await _somethingIsRunningTask;
_somethingIsRunningTask = DoSomethingAsync(_cts.Token);
await _somethingIsRunningTask;
}
private static int _numberOfStarts;
private async Task DoSomethingAsync(CancellationToken cancellationToken)
{
_numberOfStarts++;
int numberOfStarts = _numberOfStarts;
try
{
Console.WriteLine(numberOfStarts + " Start to do something...");
await Task.Delay(TimeSpan.FromSeconds(1)); // This operation can not be cancelled.
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
Console.WriteLine(numberOfStarts + " Finished to do something...");
}
catch (OperationCanceledException)
{
Console.WriteLine(numberOfStarts + " Cancelled to do something...");
}
}
Фактический вывод при трехкратном вызове RestartAsync выглядит следующим образом (обратите внимание, что второй запуск отменяет и ожидает первый, но в то же время третий запуск также ожидает первый вместо отмены и ожидания второго):
1 Start to do something...
1 Cancelled to do something...
2 Start to do something...
3 Start to do something...
2 Finished to do something...
3 Finished to do something...
Но я хочу добиться этого:
1 Start to do something...
1 Cancelled to do something...
2 Start to do something...
2 Cancelled to do something...
3 Start to do something...
3 Finished to do something...
Мое текущее решение следующее:
private async Task RestartAsync()
{
if (_isRestarting)
{
return;
}
_cts.Cancel();
_cts = new CancellationTokenSource();
_isRestarting = true;
await _somethingIsRunningTask;
_isRestarting = false;
_somethingIsRunningTask = DoSomethingAsync(_cts.Token);
await _somethingIsRunningTask;
}
Тогда я получаю этот вывод:
1 Start to do something...
1 Cancelled to do something...
2 Start to do something...
2 Finished to do something...
Теперь, по крайней мере, DoSomethingAsync
не запускается, пока он еще выполняется (обратите внимание, что третий запуск игнорируется, что не имеет большого значения, потому что в противном случае он должен отменить второй запуск).
Но это решение нехорошо, и мне приходится повторять этот уродливый шаблон везде, где я хочу такого поведения. Есть ли какой-нибудь хороший шаблон или структура для такого рода механизма перезапуска?