Задача службы Windows — отмена опроса

Я пишу службу Windows и нашел пример, который предлагает написать службы Windows опроса следующим образом:

private void Poll()
{
    CancellationToken cancellationPoll = ctsPoll.Token;
    while (!cancellationPoll.WaitHandle.WaitOne(tsInterval))
    {
        PollDatabase();
        // Occasionally check the cancellation state.
        if (cancellationPoll.IsCancellationRequested)
        {
            break;
        }
    }
}

Я немного запутался, когда дело доходит до отмены, и если мне нужны и cancelPoll.WaitHandle.WaitOne(), и cancelPoll.IsCancellationRequested, или они делают одно и то же, и требуется только один?

Вы хотите приостановить выполнение между опросами?

Ackdari 21.03.2019 18:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
243
3

Ответы 3

!cancellationPoll.WaitHandle.WaitOne(tsInterval) нужен, чтобы не ждать все время. WaitOne(tsInterval) вернется либо потому, что токен получил сигнал об отмене, либо потому, что время истекло. Если токен получил сигнал на отмену, WaitOne(tsInterval) вернет true и завершит цикл.

Например, если вы сделаете что-то вроде:

while(true)
{
    // long operation
    if (cancellationPoll.IsCancellationRequested)
    {
        break;
    }

    Thread.Sleep(tsInterval);
}

если затем отмена запрашивается, когда поток заблокирован Thread.Sleep(), вся операция не будет знать, что отмена запрошена, пока Thread.Sleep() не завершится и следующий цикл не перейдет к оператору if.

Thread.Sleep там спекуляция, неизвестно нужна она или нет. Учитывая, что PollDatabase подразумевает некоторый ввод-вывод, он вообще не нужен. Но даже если вам нужна функциональность для приостановки планирования потока, в общем случае обычно используется очень короткий период, который измеряется десятками миллисекунд или меньше, и, учитывая, что сигнал отмены приходит один раз, в среднем сценарии вы получить половину этого периода потери только один раз, что в подавляющем большинстве известных сценариев отмены просто можно пренебречь.

Dmytro Mukalov 21.03.2019 18:31

@DmytroMukalov Учитывая, что в исходном коде tsInterval дается WaitOne(), я не ошибаюсь, предполагая, что должна быть приостановка выполнения.

Ackdari 21.03.2019 18:41

Ожидание WaitHanlde здесь избыточно, так как с точки зрения результата оно делает то же самое, что и IsCancellationRequested — указывает, что запрашивается отмена (но делает это немного по-другому). Так что для вашего случая вы можете выбрать один метод: либо WaitHandle, либо IsCancellationRequested. Но имейте в виду, что WaitHandle — это IDisposable и требует удаления связанного CancellationTokenSource. Если вы решите использовать IsCancellationRequested, не забудьте добавить вызов, который должен перепланировать поток, такой как Thread.Sleep, чтобы не перегружать ресурсы ЦП. Один из сценариев, когда можно применить WaitHanlde, — это когда вам нужно дождаться дескриптора и вы хотели бы ввести семантику отмены для этого ожидания:

 WaitHandle.WaitAny(new [] { handleToWait, cancellationHandle });

Неправильно, что оба подхода делают то же самое. Решение с дескриптором ожидания решает проблему, заключающуюся в возможности отмены, пока поток ожидает следующей итерации опроса. Решение IsCancellationRequested решает проблему отмены во время выполнения итерации опроса.

Ackdari 21.03.2019 17:39

@Ackdari Если вы внимательно прочитаете, вы увидите, что я указал, что они делают то же самое с точки зрения результата - вы можете проверить это в исходном коде. Они делают это по-разному - это именно то, что я сказал - так что не так с моим утверждением?

Dmytro Mukalov 21.03.2019 17:47

@DmytroMualov неправильно утверждать, что оба решения ведут себя одинаково. Если вы замените WaitOne() на true и добавите вызов Thread.Sleep(), скажем, время ожидания составляет 5 минут. Это приведет к тому, что операция была отменена, но он не будет знать об этом в течение 5 минут.

Ackdari 21.03.2019 18:01

@Ackdari К сожалению, вы не можете внимательно читать, потому что поведение и результат - не одно и то же. Зачем ты вообще упомянул Thread.Sleep? Это не имеет ничего общего с токеном отмены. Я добавил его в качестве примера вызова для удаления потока из очереди планирования, но не для функции ожидания токена отмены. Если есть другие вызовы перепланирования потоков (IO, операции ожидания), вам это не нужно. Функциональность ожидания отмены в течение значительного периода времени вообще избыточна, если вы не ждете другого дескриптора, поэтому пример с 5 минутами не имеет значения.

Dmytro Mukalov 21.03.2019 18:14

!cancellationPoll.WaitHandle.WaitOne(tsInterval) предназначен для обеспечения интервала опроса, поэтому у вас будет как минимум tsIntetval между опросами (+ продолжительность операции):

--tsInterval--|--operation--|--tsInterval--|...

Если вы посмотрите на документацию для CancellationToken.WaitHandle, там написано следующее:

A WaitHandle that is signaled when the token is canceled.

Итак, в вашем случае достаточно операции cancellationPoll.IsCancellationRequested, потому что после нее у вас ничего нет. Но представьте себе такую ​​ситуацию:

while (!cancellationPoll.WaitHandle.WaitOne(tsInterval))
{
    //long operation A

    if (cancellationPoll.IsCancellationRequested)
    {
        break;
    }

    //long operation B

    if (cancellationPoll.IsCancellationRequested)
    {
        break;
    }
    //long operation C
}

В этом случае имеет смысл время от времени проверять состояние отмены, чтобы избежать длительной работы...

Я все еще не вижу большого смысла, потому что вы можете поставить третий `IsCancellationRequested` для согласованности обработки и избежать путаницы, подобной возникшей в этой теме.

Dmytro Mukalov 21.03.2019 14:39

Дело в том, что вы не хотите даже запускать действие B или действие C, если токен был сигнализирован, поэтому вы не хотите ждать следующей итерации цикла... Да, вы можете поставить и третий, но выигрыш ничтожен...

Johnny 21.03.2019 14:44

Я рассматриваю рассуждения только с точки зрения IsCancellationRequested vs WaitHandle и не вижу смысла смешивать подходы, так как смешивание имеет очевидные недостатки и одного и того же результата можно добиться, только используя 3 из IsCancellationRequested.

Dmytro Mukalov 21.03.2019 14:47

@DmytroMukalov Каковы очевидные недостатки?

Johnny 21.03.2019 14:57

Наиболее очевидным является удобочитаемость — вы видите, что это сбивает с толку, хотя должно быть легко читаемым. Если ваш читатель кода знаком с одним подходом и не знаком с другим, это заставит его углубиться в детали каждого метода. Для меня это достаточное оправдание, чтобы избежать этого, но, кроме того, есть аспекты, связанные с перераспределением ресурсов - если CancellationToken исходит от внешних компонентов, которые вы не контролируете, есть вероятность, что жизненный цикл CancellationTokenSource может быть реализован неправильно, и доступ к WaitHandle вам неявно выделять новый ресурс внутри CTS.

Dmytro Mukalov 21.03.2019 15:21

Хорошо читаемость, хорошо, но фактическое требование - иметь интервал опроса. Итак, вы хотите использовать другой подход, чем WaitHandle тогда?

Johnny 21.03.2019 15:27

Я не вижу интервала опроса в требованиях.

Dmytro Mukalov 21.03.2019 15:32
while (!cancellationPoll.WaitHandle.WaitOne(tsInterval)) у вас tsInterval это интервал опроса...
Johnny 21.03.2019 15:35

Этот код взят из примера, который автор где-то нашел, и автор спрашивает об актуальности этого кода, а не об источнике требований.

Dmytro Mukalov 21.03.2019 15:38

Другие вопросы по теме