Если с сервера долго не приходят данные нужно подать сигнал.
Имитировал сервер циклом, если данные приходят (новый цикл), то должен отменяться оператор
Здравствуйте, Mystic Artifact, Вы писали: MA> Потому что в цикле i всегда равно нолю?
Спасибо, исправил. Писал While, а для форума решил исправить на for, ошибку не заметил.
Вопрос остается. Почему нет отмены?
Здравствуйте, 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: Понятно, что мои исправления так же грязны как и исходный пример.
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
Кстати компилятор на это ругается. Мораль: если встречаешь непонятное поведение и ругань компилятора — рассматривай это как ошибки, и разбирайся почему варнинги получились и как их правильно устранить.
Здравствуйте, Mystic Artifact
Спасибо. Код последнего метода работает. Насколько я понял все дело в том, что надо было не Wait, а await. MA> 2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене...
Метод с v.Wait(ct); не работает и исключение не бросается. MA> 3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.
Если код верен, то задача снимается. Т.е. проверку надо делать во время отладки и проверки кода и после, в рабочей версии, проверка не нужна?
Здравствуйте, 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 следует всегда диспозить.
Здравствуйте, Mystic Artifact
Спасибо. Код последнего метода работает. Насколько я понял все дело в том, что надо было не Wait, а await. MA>> 2. В SignalNoServer v.Wait(ct) — бросает исключение при отмене...
Метод с v.Wait(ct); не работает и исключение не бросается. MA>> 3. task.Dispose(); — нельзя делать без проверки состояния задачи, иначе получишь опять исключение.
Если код верен, то задача снимается. Т.е. проверку надо делать во время отладки и проверки кода и после, в рабочей версии, проверка не нужна?
А вообще схема правильная решения задачи или можно решить без вызова исключений? Если вместо Task сделать поток, там тоже будет исключение или с потоками код был бы более верным решением?
Здравствуйте, Passerby, Вы писали:
P>А вообще схема правильная решения задачи или можно решить без вызова исключений?
Специально провоцировать их не стоит. Но и специально их избегать, без необходимости — так же смысла не имеет.
По сути всё сводится к метрике: сколько исключений генерируется в секунду и она в среднем должна быть адекватной (около ноля).
А насчет схемы в целом — то, ну, в целом как бы похоже, но не совсем. У меня такое чувство, что ты пытаешься сделать обычное чтение с таймаутом, и отсюда вопрос — это что/чем ты читаешь, что готового таймаута нет?
Ну, а если ты хочешь сделать ReadAsync с таймаутом руками, то цикл в Main не должен вообще манипулировать CTS (по крайней мере не в таком смысле).
CTS создается, в ReadAsync, запускается таймер (например через Task.Delay, но проще/лучше использовать CancelAfter), делается ее продолжение отменяющий CTS (в случае если используется Task.Delay). И запускается задача/настоящее чтение которая может быть отменена по таймауту (посредством только что созданного CTS). Еще не забываем, что в ReadAsync мы можем захотеть передать свой токен, и теперь нам нужно уже ждать на двух токенах — т.е. в самом начале создаем Linked CTS, а не обычный. В общем, оно не сложно делается, стандартный подход для библиотечного кода. Но я не понял точно ли это тебе нужно.
Здравствуйте, Mystic Artifact, Вы писали: MA> По сути всё сводится к метрике: сколько исключений генерируется в секунду и она в среднем должна быть адекватной (около ноля).
С этим плохо: несколько десятков исключений в секунду.
Клиент подписан по WebSocket на данные сервера, которые приходят часто без четкого периода (т.к. WebSocket данные не запрашиваются, а один раз на них подписываются): обычно несколько десятков новых данных в секунду, т.е несколько десятков событий. Если рвется связь или что-то, что вынуждает сервер удалить подписку, нужно подать сигнал, чтобы человек определил, что происходит. Более 2 секунд вряд ли могут не приходить новые данные. Т.е. если в течение 2 сек приходит хотя бы одно новые данное (запускается обработчик такого события) все норм, если же нет такого события, то подать сигнал. Вот в методе обрабатывающим события я и хочу сделать примерно то, что написал.
Здравствуйте, Passerby, Вы писали:
P>С этим плохо: несколько десятков исключений в секунду.
Это не смертельно. Оно может прожевать гораздо больше. Не очень оптимально — наверное... Но выбора особо нет.
P>Клиент подписан по WebSocket на данные сервера, которые приходят часто без четкого периода (т.к. WebSocket данные не
Да, загадка. Про WebSocket не скажу ничего — мало использовал, и при первой открывшейся возможности убежал от него.
Ну коли так, то в принципе похоже.
Я так понял сбои возникают довольно часто? По неизвестным причинам или это твоё личное ноу-хау? Возможно стоит посылать пинг-понг, вместо простого таймаута?
Здравствуйте, Passerby, Вы писали:
P>Здравствуйте, varenikAA, Вы писали: P>>> cts = new(); AA>>Вот это зачем? непонятно. P>А как иначе сформировать новый токен?
если я правильно понял, на второй итерации таска отменяется, т.е. второй раз токен уже не понадобится,
ну и почему нельзя использовать один токен для main? по-моему одного достаточно.
Здравствуйте, Mystic Artifact, Вы писали: MA> Да, загадка. Про WebSocket не скажу ничего — мало использовал, и при первой открывшейся возможности убежал от него.
Почему убежали? Задержки запросов по HttpClient слишком большие: приходят уже устаревшие данные. MA> Я так понял сбои возникают довольно часто? По неизвестным причинам или это твоё личное ноу-хау? Возможно стоит посылать пинг-понг, вместо простого таймаута?
Сбои были раз в день. По неизвестным причинам: возможно, плохо написал обработчик событий, а может на секунду рвется связь и сервер отписывает. Пинг-понг не спасет: он может быть, а на сервере кратковременно что-то произошло и он отписал клиента. Сейчас подумываю заменить схему следующей: выделить промежуток времени и следить были ли события. Если событий не было подавать сигнал. Т.е. не прерывать промежуток времени с каждым новым событием, а новый интервал запускать после завершения предыдущего. Но все равно такая схема может быть не всегда: серверу дается указание что-то сделать, если у него есть такая возможность, которая зависит от актуальных данных. По истечении времени, если сервер это не сделал, задача серверу снимается.
Здравствуйте, varenikAA, Вы писали: AA> на второй итерации таска отменяется, т.е. второй раз токен уже не понадобится,
На второй итерации таск отменяется. И на 3-й, и на 4-й, и т.д., но на какой-нибудь итерации таск не должен отмениться и программа должна в этот момент подать сигнал.
Здравствуйте, Passerby, Вы писали:
P>Почему убежали? Задержки запросов по HttpClient слишком большие: приходят уже устаревшие данные.
И да и нет. Просто исторически, его использование было продиктовано третьей стороной, но схема со временем выродилась полностью в внутри-процессное, взаимодействие, и эта кухня стала ненужна.
Здравствуйте, Passerby, Вы писали:
P>Сбои были раз в день. По неизвестным причинам:
Что за сервер вебсокета? если IIS там пул приложений на котором висит сайт по умолчанию каждые 24 часа перезапускается, проверьте в настройках.
и я так понял у вас не сигналР, почему? сервер чужой?
вообще может попробовать так
var timeOut = new CancellationTokenSource(5_000).Token;
т.е. без цикла. если известно что данные не реже чем раз в 2 секунды приходят?
Здравствуйте, varenikAA, Вы писали: AA>и я так понял у вас не сигналР, почему? сервер чужой?
СигналР. Сервер чужой, в другой стране. AA> var timeOut = new CancellationTokenSource(5_000).Token;
Зачем?