0

Я запускаю в отдельном потоке выполняться метод, в процессе выполнения которого возникает ситуация, когда нужно дождаться освобождения ресурса. В этом случае я запускаю ожидание через 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)
        {

        }
    }
}

}

На данный момент я нашел два способа добиться того что я хочу:

  1. В обработке исключения добавить await Task.Delay(1); - в этом случае прерывающий поток обрабатывает ошибку и продолжает свою работу, а заблокированный поток возобновляет свою работу.
  2. При прерывании ожидания создавать новый поток 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++;
        }
    }
}

  • Текущие способы похожи на костыли > async void... – EvgeniyZ Mar 14 '21 at 17:04
  • Проблема этого кода в том что в консоль не выводится сообщение "Press AnyKey to end". При отладке обнаружил что при отмене ожидания происходит возобновление асинхронного метода, которое застревает в бесконечном цикле. – Анатолий Гладких Mar 15 '21 at 19:11
  • удалось разобраться? – aepot Mar 19 '21 at 22:03
  • @aepot, нет, вариант с поллингом мне не подходит, потому что я как раз стараюсь отказаться от него. Пока что остановился на await Task.Delay(1);. – Анатолий Гладких Mar 22 '21 at 05:46
  • А у вас нет других вариантов, либо поллинг, либо блокировка потока ожиданием клавиши. Ну как знаете, удаляю ответ. – aepot Mar 22 '21 at 06:33
  • Проблема блокировки ожиданием клавиши в том, что не разблокировать поток не нажав клавишу, хотя-бы виртуально. Кстати, а вак вы поняли, что Console.KeyAvailable вам не походит? – aepot Mar 22 '21 at 06:40
  • Кстати, ваш пример из вопроса, который вы туда дописали, не рабочий, ваш токен отмены ничего не отменяет. Ваш Method() досчитает до конца, что бы вы не делали. А следовательно смысла 0 во всем этом. Представьте, что Method() работает 10 секунд. Засуньте туда паузы, выведите конечную сумму в консоль в конце концов. Что отменять то собрались? Вопрос же про отмену был? Создайте рабочий пример сначала, типа того что я вам написал, но на этот раз сами. Попробуйте реально отменить что-то, а не фейково. – aepot Mar 22 '21 at 06:51
  • Представьте операцию на 10 секунд, если вы не вызвали отмену, то операция завершилась успешно. Если вызвали - завершилась сразу как вызвали. В этом и есть смысл токена отмены. И да CancellationTokenSource - IDisposable и содержит неуправляемые ресурсы: Использование объектов, реализующих IDisposable – aepot Mar 22 '21 at 06:59
  • @aepot, Мне необходимо отменить бесконечное ожидание ресурса await Task.Delay(Timeout.Infinite, WaitResourceTokenSource.Token);, а метод WorkAsync должен продолжить свою работу. Проблема в том что поток, который прерывает ожидание, застревает в методе WorkAsync, а мне необходимо чтоб он разблокировал спящий поток, после чего продолжил бы выполнение. – Анатолий Гладких Mar 23 '21 at 09:20
  • Вы пока не поняли, как это работает, несмотря на то что я вам показывал. Вы говорите о блокировке потока, но ваш вопрос про асинхронное программирование, где блокировка потока - сама по себе нонсенс. Еще раз, вот документация, читайте, там есть ссылки на другие документы, их тоже читайте. – aepot Mar 23 '21 at 09:28

0 Answers0