Почему не отменяется задача?
От: Passerby  
Дата: 15.03.21 18:15
Оценка:
Если с сервера долго не приходят данные нужно подать сигнал.
Имитировал сервер циклом, если данные приходят (новый цикл), то должен отменяться оператор
Console.WriteLine("Обработка закончена " + DateTime.Now);

Почему отмены нет?
static async Task Main(string[] args)
    {
      CancellationTokenSource cts = new CancellationTokenSource();
      Task task = null;
      for(int i=0; ;i++)
      {
        if (i != 0)
        {
         cts.Cancel();
         Thread.Sleep(10);
         task.Dispose();
        }
        cts = new();
        task = SignalNoServer(cts.Token);        
      }
}
static async Task SignalNoServer(CancellationToken ct)
    {
      var v = Task.Delay(2000, ct);
      v.Wait(ct);
      if (!v.IsCanceled) Console.WriteLine("Обработка закончена " + DateTime.Now);
//или другой вариант, в котором тоже нет отмены
//if (!ct.IsCancellationRequested) Console.WriteLine("Обработка закончена " + DateTime.Now);
      v.Dispose();
    }
Отредактировано 15.03.2021 20:52 Passerby . Предыдущая версия . Еще …
Отредактировано 15.03.2021 20:36 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 20:36 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 20:35 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 20:33 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 18:19 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 18:18 Passerby . Предыдущая версия .
Re: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 15.03.21 20:43
Оценка: 1 (1)
Здравствуйте, Passerby, Вы писали:

P>Почему отмены нет?

Потому что в цикле i всегда равно нолю?
Re[2]: Почему не отменяется задача?
От: Passerby  
Дата: 15.03.21 20:55
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:
MA> Потому что в цикле i всегда равно нолю?
Спасибо, исправил. Писал While, а для форума решил исправить на for, ошибку не заметил.
Вопрос остается. Почему нет отмены?
Re[3]: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 15.03.21 21:27
Оценка: 14 (3) +1
Здравствуйте, Passerby, Вы писали:

P>Здравствуйте, Mystic Artifact, Вы писали:

MA>> Потому что в цикле i всегда равно нолю?
P>Спасибо, исправил. Писал While, а для форума решил исправить на for, ошибку не заметил.
P>Вопрос остается. Почему нет отмены?

1. async методы всегда выполняются синхронно в месте вызова, до точки разрыва (await). Т.е. в таком состоянии твой код просто исполняется синхронно, и отмена не происходит просто потому, что, там некому его отменять. Поставь Console.WriteLine(i) в цикле, что бы видеть кол-во происходящих итераций.

2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене (не уверен, что это предполагалось).

3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.

В примере ниже в цикле запускается целая пачка SignalNoServer потому, что в твоём изначальном примере так и предполагалось видимо (но отличается от того что ты описывал словами).

        static async Task Main(string[] args)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            Task task = null;
            for (int i = 0; ; i++)
            {
                Console.WriteLine(i);

                if (i != 0)
                {
                    cts.Cancel();
                    Thread.Sleep(10);
                    // task.Dispose();
                }

                cts = new();
                task = SignalNoServer(cts.Token);
            }
        }

        static async Task SignalNoServer(CancellationToken ct)
        {
            await Task.Yield(); // безусловный разрыв

            var v = Task.Delay(2000, ct);

            try
            {
                v.Wait(ct);
            }
            catch (OperationCanceledException ocex)
            {
            }

            if (!v.IsCanceled) Console.WriteLine("Обработка закончена " + DateTime.Now);
            else Console.WriteLine("Cancelled: " + DateTime.Now);
            //или другой вариант, в котором тоже нет отмены
            //if (!ct.IsCancellationRequested) Console.WriteLine("Обработка закончена " + DateTime.Now);
            v.Dispose();
        }


PS: Вместо таких городушек — можно было бы просто написать await.

        static async Task SignalNoServer(CancellationToken ct)
        {
            var v = Task.Delay(2000, ct);
            try
            {
                await v;
            }
            catch (OperationCanceledException) { }

            // if (ct.IsCancellationRequested) Console.WriteLine("Cancellation requested " + DateTime.Now);
            if (!v.IsCanceled) Console.WriteLine("Обработка закончена " + DateTime.Now);
            else Console.WriteLine("Cancelled: " + DateTime.Now);
            //или другой вариант, в котором тоже нет отмены
            //if (!ct.IsCancellationRequested) Console.WriteLine("Обработка закончена " + DateTime.Now);
            v.Dispose();
        }


PPS: Понятно, что мои исправления так же грязны как и исходный пример.
Отредактировано 15.03.2021 22:13 Mystic Artifact . Предыдущая версия .
Re: Почему не отменяется задача?
От: hi_octane Беларусь  
Дата: 15.03.21 21:58
Оценка: 14 (2) +1
Здравствуйте, Passerby, Вы писали:
task = SignalNoServer(cts.Token);
//один writeline впиши
//увидишь что пока SignalNoServer полностью не выполнится 
//новой итерации цикла не будет.
Console.WriteLine("Выход из метода SignalNoServer " + DateTime.Now);


Метод SignalNoServer ты объявил асинхронным, но при этом ожидаешь завершения задачи синхронным Task.Wait.
Смешивать синхронику и асинхронику можно только когда очень чётко и в деталях понимаешь зачем тебе это надо.

Асинхронные методы исполняются абсолютно также как обычные до return, если по пути не встретился await. У тебя await v; нету, есть только синхронный v.Wait, поэтому метод SignalNoServer полностью исполняет v.Wait (т.е. ждёт сколько сказали), затем проверяет IsCancelled (Main всё ещё не получил управления), и затем SignalNoServer WriteLine и выход (т.е. возвращается в Main), после чего цикл в Main типа отменяет уже задачу, но отменять нечего — задача полностью выполнилась. А тебе кажется что задача не отменяется.

Лечится через замену v.Wait на await v; ну и ловлю TaskCancelledException

Кстати компилятор на это ругается. Мораль: если встречаешь непонятное поведение и ругань компилятора — рассматривай это как ошибки, и разбирайся почему варнинги получились и как их правильно устранить.
Re[4]: Почему не отменяется задача?
От: Passerby  
Дата: 15.03.21 22:34
Оценка:
Здравствуйте, Mystic Artifact
Спасибо. Код последнего метода работает. Насколько я понял все дело в том, что надо было не Wait, а await.
MA> 2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене...
Метод с v.Wait(ct); не работает и исключение не бросается.
MA> 3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.
Если код верен, то задача снимается. Т.е. проверку надо делать во время отладки и проверки кода и после, в рабочей версии, проверка не нужна?
Отредактировано 15.03.2021 22:46 Passerby . Предыдущая версия . Еще …
Отредактировано 15.03.2021 22:39 Passerby . Предыдущая версия .
Отредактировано 15.03.2021 22:37 Passerby . Предыдущая версия .
Re[5]: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 15.03.21 22:52
Оценка: 7 (1)
Здравствуйте, Passerby, Вы писали:

P>Спасибо. Насколько я понял все дело в том, что надо было не Wait (в этом случае метод синхронен), а await.

По сути — да, ниже hi_octane очень хорошо объяснил.

MA>> 2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене...

P>Метод с v.Wait(ct); не работает и исключение не бросается.
Как только исправишь (1) / вставишь Task.Yield — т.е. как только действительно начнет случаться отмена — исключения не заставят себя ждать. Всё остальное ровно от этого.

MA>> 3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.

P>Если код верен, то задача снимается. Т.е. проверку надо делать во время отладки и проверки кода и после, в рабочей версии, проверка не нужна?
task.Dispose() в подавляющем числе случаев вызывать не нужно: https://devblogs.microsoft.com/pfxteam/do-i-need-to-dispose-of-tasks/ .
А вот CancellationTokenSource следует всегда диспозить.
Re[5]: Почему не отменяется задача?
От: Passerby  
Дата: 15.03.21 22:59
Оценка:
Здравствуйте, Mystic Artifact
Спасибо. Код последнего метода работает. Насколько я понял все дело в том, что надо было не Wait, а await.
MA>> 2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене...
Метод с v.Wait(ct); не работает и исключение не бросается.
MA>> 3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.
Если код верен, то задача снимается. Т.е. проверку надо делать во время отладки и проверки кода и после, в рабочей версии, проверка не нужна?

А вообще схема правильная решения задачи или можно решить без вызова исключений? Если вместо Task сделать поток, там тоже будет исключение или с потоками код был бы более верным решением?
Отредактировано 15.03.2021 23:07 Passerby . Предыдущая версия . Еще …
Отредактировано 15.03.2021 23:00 Passerby . Предыдущая версия .
Re[6]: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 16.03.21 00:22
Оценка:
Здравствуйте, Passerby, Вы писали:

P>А вообще схема правильная решения задачи или можно решить без вызова исключений?

Специально провоцировать их не стоит. Но и специально их избегать, без необходимости — так же смысла не имеет.

По сути всё сводится к метрике: сколько исключений генерируется в секунду и она в среднем должна быть адекватной (около ноля).

А насчет схемы в целом — то, ну, в целом как бы похоже, но не совсем. У меня такое чувство, что ты пытаешься сделать обычное чтение с таймаутом, и отсюда вопрос — это что/чем ты читаешь, что готового таймаута нет?

Ну, а если ты хочешь сделать ReadAsync с таймаутом руками, то цикл в Main не должен вообще манипулировать CTS (по крайней мере не в таком смысле).

CTS создается, в ReadAsync, запускается таймер (например через Task.Delay, но проще/лучше использовать CancelAfter), делается ее продолжение отменяющий CTS (в случае если используется Task.Delay). И запускается задача/настоящее чтение которая может быть отменена по таймауту (посредством только что созданного CTS). Еще не забываем, что в ReadAsync мы можем захотеть передать свой токен, и теперь нам нужно уже ждать на двух токенах — т.е. в самом начале создаем Linked CTS, а не обычный. В общем, оно не сложно делается, стандартный подход для библиотечного кода. Но я не понял точно ли это тебе нужно.
Re: Почему не отменяется задача?
От: varenikAA  
Дата: 16.03.21 04:07
Оценка: +1
Здравствуйте, Passerby, Вы писали:

P>

P>        cts = new();
  
 
P>


Вот это зачем? непонятно.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[7]: Почему не отменяется задача?
От: Passerby  
Дата: 16.03.21 06:20
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:
MA> По сути всё сводится к метрике: сколько исключений генерируется в секунду и она в среднем должна быть адекватной (около ноля).
С этим плохо: несколько десятков исключений в секунду.
Клиент подписан по WebSocket на данные сервера, которые приходят часто без четкого периода (т.к. WebSocket данные не запрашиваются, а один раз на них подписываются): обычно несколько десятков новых данных в секунду, т.е несколько десятков событий. Если рвется связь или что-то, что вынуждает сервер удалить подписку, нужно подать сигнал, чтобы человек определил, что происходит. Более 2 секунд вряд ли могут не приходить новые данные. Т.е. если в течение 2 сек приходит хотя бы одно новые данное (запускается обработчик такого события) все норм, если же нет такого события, то подать сигнал. Вот в методе обрабатывающим события я и хочу сделать примерно то, что написал.
Re[2]: Почему не отменяется задача?
От: Passerby  
Дата: 16.03.21 06:23
Оценка:
Здравствуйте, varenikAA, Вы писали:
P>> cts = new();
AA>Вот это зачем? непонятно.
А как иначе сформировать новый токен?
Re[8]: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 16.03.21 07:25
Оценка:
Здравствуйте, Passerby, Вы писали:

P>С этим плохо: несколько десятков исключений в секунду.

Это не смертельно. Оно может прожевать гораздо больше. Не очень оптимально — наверное... Но выбора особо нет.

P>Клиент подписан по WebSocket на данные сервера, которые приходят часто без четкого периода (т.к. WebSocket данные не


Да, загадка. Про WebSocket не скажу ничего — мало использовал, и при первой открывшейся возможности убежал от него.

Ну коли так, то в принципе похоже.

Я так понял сбои возникают довольно часто? По неизвестным причинам или это твоё личное ноу-хау? Возможно стоит посылать пинг-понг, вместо простого таймаута?
Re[3]: Почему не отменяется задача?
От: varenikAA  
Дата: 16.03.21 08:16
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Здравствуйте, varenikAA, Вы писали:

P>>> cts = new();
AA>>Вот это зачем? непонятно.
P>А как иначе сформировать новый токен?

если я правильно понял, на второй итерации таска отменяется, т.е. второй раз токен уже не понадобится,
ну и почему нельзя использовать один токен для main? по-моему одного достаточно.
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[9]: Почему не отменяется задача?
От: Passerby  
Дата: 16.03.21 10:53
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:
MA> Да, загадка. Про WebSocket не скажу ничего — мало использовал, и при первой открывшейся возможности убежал от него.
Почему убежали? Задержки запросов по HttpClient слишком большие: приходят уже устаревшие данные.
MA> Я так понял сбои возникают довольно часто? По неизвестным причинам или это твоё личное ноу-хау? Возможно стоит посылать пинг-понг, вместо простого таймаута?
Сбои были раз в день. По неизвестным причинам: возможно, плохо написал обработчик событий, а может на секунду рвется связь и сервер отписывает. Пинг-понг не спасет: он может быть, а на сервере кратковременно что-то произошло и он отписал клиента. Сейчас подумываю заменить схему следующей: выделить промежуток времени и следить были ли события. Если событий не было подавать сигнал. Т.е. не прерывать промежуток времени с каждым новым событием, а новый интервал запускать после завершения предыдущего. Но все равно такая схема может быть не всегда: серверу дается указание что-то сделать, если у него есть такая возможность, которая зависит от актуальных данных. По истечении времени, если сервер это не сделал, задача серверу снимается.
Re[4]: Почему не отменяется задача?
От: Passerby  
Дата: 16.03.21 11:00
Оценка:
Здравствуйте, varenikAA, Вы писали:
AA> на второй итерации таска отменяется, т.е. второй раз токен уже не понадобится,
На второй итерации таск отменяется. И на 3-й, и на 4-й, и т.д., но на какой-нибудь итерации таск не должен отмениться и программа должна в этот момент подать сигнал.
Re[10]: Почему не отменяется задача?
От: Mystic Artifact  
Дата: 16.03.21 14:18
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Почему убежали? Задержки запросов по HttpClient слишком большие: приходят уже устаревшие данные.

И да и нет. Просто исторически, его использование было продиктовано третьей стороной, но схема со временем выродилась полностью в внутри-процессное, взаимодействие, и эта кухня стала ненужна.
Re[10]: Почему не отменяется задача?
От: varenikAA  
Дата: 16.03.21 14:25
Оценка:
Здравствуйте, Passerby, Вы писали:

P>Сбои были раз в день. По неизвестным причинам:


Что за сервер вебсокета? если IIS там пул приложений на котором висит сайт по умолчанию каждые 24 часа перезапускается, проверьте в настройках.
и я так понял у вас не сигналР, почему? сервер чужой?
вообще может попробовать так
   var timeOut = new CancellationTokenSource(5_000).Token;

т.е. без цикла. если известно что данные не реже чем раз в 2 секунды приходят?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re[11]: Почему не отменяется задача?
От: Passerby  
Дата: 16.03.21 15:32
Оценка:
Здравствуйте, varenikAA, Вы писали:
AA>и я так понял у вас не сигналР, почему? сервер чужой?
СигналР. Сервер чужой, в другой стране.
AA> var timeOut = new CancellationTokenSource(5_000).Token;
Зачем?
Re[10]: Почему не отменяется задача?
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 16.03.21 16:10
Оценка: 2 (1)
Здравствуйте, Passerby, Вы писали:

https://stackoverflow.com/questions/49786269/signalr-core-check-connection-state-from-webapi

In .NET Core 2.1 at least, you can check the State property of your HubConnection:


if (_connection.State == HubConnectionState.Disconnected) {
        await _connection.StartAsync();
}
и солнце б утром не вставало, когда бы не было меня
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.