Альтернативы использованию Thread.Sleep для ожидания - программирование
Подтвердить что ты не робот

Альтернативы использованию Thread.Sleep для ожидания

Во-первых, я не задаю тот же вопрос, что С# - Альтернатива Thread.Sleep? или Альтернатива Thread. Сон в С#?. Я не думаю, что использую его неправильно и нуждаюсь в подлинной альтернативе для конкретных ситуаций.

Во время анализа кода я увидел удивительное нарушение:

Использование Thread.Sleep() является признаком ошибочной конструкции.

Это нарушение приводит к статье Питера Ричи о том, почему именно это составляет плохую конструкцию.

Мы все знаем, что создание потоков дорого, а блокировка потоков - это соперничество в пуле. Мы также знаем, что каждый поток будет выделять мегабайт памяти, поэтому он должен иметь короткий срок службы, блокировка пользовательского интерфейса - это зло, использование сна для синхронизации ненадежное и т.д. И т.д. И т.д. Это приводит меня к моей точке, если вам действительно нужно выполнить спать, что вы должны использовать, если не Thread.Sleep?

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

У меня есть следующие ситуации в производственном коде, который использует Thread.Sleep довольно успешно:

  • Ожидание блокировки файла операционной системой (проблемы с блокировкой файлов catch, ожидание второй, повторите попытку, отпустите через некоторое время).
  • Убив процесс и ожидая его появления в списке процессов (убейте его, убедитесь, что он не работает, подождите второй, убедитесь, что он еще не запущен, заставьте его закрыть).
  • Ожидание буферов копирования для очистки (проверьте размер файла, попробуйте его получить, wait, проверьте, изменился ли размер).

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

4b9b3361

Ответ 1

Тип WaitHandle и производные типы обеспечивают механизм, управляемый событиями, который ожидает подключения к операционной системе. Например, когда у вас есть Task<T> task, и вы ждете результата, обратившись к task.Result, внутренняя реализация не проводит опрос с Thread.Sleep вызовами между ними. Он использует WaitHandle -расширенный тип для ожидания и синхронизации.

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

Просто природа программного обеспечения ждет других вещей в многопоточной среде с внешними факторами, влияющими на ваш код, иногда вам нужно ждать...

В wait все в порядке. Для ожидания с опросом часто нет (*). Если есть способ, вы можете использовать ожидающее событий событие, вы обычно должны стремиться к его использованию.

Я не очень хорошо понимаю, что именно вы спрашиваете, поэтому я не буду подробно останавливаться на этом. Если вы оставите комментарий, я могу расширить свой ответ.


(*) Теоретическая причина ожидания с опросом плоха в следующем:

Предположим, что у меня есть код, который выглядит так:

//START
Begin();
while (!Done())
    Thread.Sleep(D);
//STOP

Begin() запускает некоторую операцию. Done() return true означает, что операция завершена. Предположим, что это произойдет примерно через T. Тогда:

  • Нить просыпается и проверяет условие (вызовы Done()) T/D times
  • Продолжительность от START до STOP включает ожидаемый D/2 только из-за Thread.Sleep

Какое значение D следует выбрать? По мере увеличения D ожидаемая форма продолжительности START до STOP линейно возрастает. Когда вы уменьшаете D, число итераций (связанных с) увеличивается как 1/D. Оба они плохие, и найти правильный D проблематично.

Теперь сравните это с ожидаемым событием:

//START
Begin();
WaitDone();
//STOP
Теоретически говоря, до тех пор, пока WaitDone() каким-то волшебным образом ждет, пока операция не закончится, но уже не исчезли, обе проблемы, обнаруженные в случае ожидания с опросом, исчезли: этот поток ждет точно правильное количество времени - не больше, не меньше!

Повторить то, что я начал с: в .NET класс WaitHandle и производные типы облегчают этот подход.

Ответ 2

Хорошо, ты сказал об этом. Цитата: "Мы все знаем, что создание потоков дорого, а блокирование потоков - это соперничество в пуле", поэтому вы даже понимаете, что такое пул потоков.

Вы также понимаете, что блокировка потока пользовательского интерфейса плохая.

Еще раз посмотрим на модель пула потоков: у вас есть пул потоков, возможно, один на процессор, а затем передавайте им задания. Каким будет смысл блокировать один из этих потоков? Если у него нет работы для выполнения сейчас, тогда он должен просто перейти к другой задаче.

Итак, перейдя непосредственно к вашему вопросу "Что приводит меня к моей точке зрения, если вам действительно нужно выспаться, что вы должны использовать, если не Thread.Sleep?", в современной хорошо продуманной программе вы никогда не будете нужно сделать это, вы просто планируете задачу для последнего.

Вы должны думать о потоках в пуле, так же как о процессорах в системе, как о ресурсах, которые должны быть выпущены другим, когда это не нужно.

Переходя к вашим примерам, вы слишком вовлечены в парадигму императивного программирования.

  • Вам не нужно ждать, пока процесс исчезнет... Я не представляю, зачем вам это нужно, но если вам придется ждать, это потому, что у вас есть работа, которую нужно выполнить через некоторое время, "продолжение" вашего функция. Вы должны настроить таймер для этого "продолжения".
  • В примерах файлов должны быть другие механизмы для него, если у них их нет... это будет хороший дизайн ОС. Например, единственный безопасный способ дождаться сброса буферов - это примитив ОС, например fsync.
  • Если кто-то пишет файл, а затем еще одно чтение из файла, то необходим механизм синхронизации, а не время ожидания (если только файл не является только подключением, и в этом случае сам файл является механизмом синхронизации).

Ожидание механизма синхронизации не является "плохим".

Ответ 3

В одном из моих проектов я использовал 2 потока, и у меня возникла проблема с UI-замораживанием thnx в Thread.Sleep.... это исправило мою проблему:

    public static void Sleeping(int miliseconds)
    {
        var task = Sleep(miliseconds);
        task.Wait();
    }

    public static async Task Sleep(int miliseconds)
    {
        await Task.Delay(miliseconds);
    }