Как правильно обработать OperationCanceledException при отмене операции?
Я запускаю в отдельном потоке выполняться метод, в процессе выполнения которого возникает ситуация, когда нужно дождаться освобождения ресурса. В этом случае я запускаю ожидание через Task.Delay(Timeout.Infinite, source.Token), который потом прервется из другого потока, но у меня возникла проблема. После того как другой поток прерывает ожидание, он застревает в бесконечном цикле метода, вместо того чтоб разблокировать поток и продолжить выполнение.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace DelayInfinite
{
class Program
{
private static CancellationTokenSource source;
static void Main(string[] args)
{
Task.Run(Work);
Console.ReadKey();
source.Cancel();
Console.WriteLine("Press AnyKey to end");
Console.ReadKey();
}
public static async void Work()
{
try
{
Console.WriteLine("Task.Delay(Infinite)");
source = new CancellationTokenSource();
await Task.Delay(Timeout.Infinite, source.Token);
}
catch
{
// ignored
}
Console.WriteLine("WhileTrue");
while (true)
{
}
}
}
}
На данный момент я нашел два способа добиться того что я хочу:
- В обработке исключения добавить
await Task.Delay(1);- в этом случае прерывающий поток обрабатывает ошибку и продолжает свою работу, а заблокированный поток возобновляет свою работу. - При прерывании ожидания создавать новый поток
Task.Run(() => source.Cancel());- в этом случае создается новый поток, который продолжает выполнение метода в отдельном потоке.
У меня возник вопрос, как правильно обработать эту ситуацию? Есть ли другие способы возобновить работу потока? Текущие способы похожи на костыли, поэтому я ищу другие способы решить эту проблему.
UPD: Сделал еще один пример
Надеюсь по нему будет понятнее что я делаю.
При выполнении этого кода в консоль не выводится "End Work", потому что основной поток застревает в методе WorkAsync после отмены ожидания.
class Program
{
private static CancellationTokenSource WaitResourceTokenSource;
private static bool IsResourceAvailable = true;
static async Task Main(string[] args)
{
var workTask = Task.Run(WorkAsync);
await Task.Delay(10);
IsResourceAvailable = false;
await Task.Delay(100);
IsResourceAvailable = true;
WaitResourceTokenSource.Cancel();
Console.WriteLine("End Work");
}
public static async Task WorkAsync()
{
Console.WriteLine("Begin Work");
while (true)
{
if (!IsResourceAvailable)
await WaitResource();
for (var i = 0; i < 5; i++)
{
Method();
}
}
}
private static async Task WaitResource()
{
Console.WriteLine("Wait");
try
{
WaitResourceTokenSource = new CancellationTokenSource();
await Task.Delay(Timeout.Infinite, WaitResourceTokenSource.Token);
}
catch
{
// ignored
}
Console.WriteLine("End Wait");
}
private static void Method()
{
var sum = 0;
for (var i = 0; i < 1000000; i++)
{
sum++;
}
}
}