Есть ли причина не использовать сон в очереди Grand Central Dispatch?

Я хотел бы сделать очередь ожидания на короткий период, пока она зацикливается. Я рассматриваю свои варианты и тестировал приостановление возобновления очереди, но для этого требуется несколько движущихся частей. Поэтому я рассматриваю возможность использовать сон или спать вместо этого. Это более общая функция потоковой передачи и хотелось бы знать, следует ли мне избегать использования сна и вместо этого использовать параметры GCD, чтобы сделать паузу в очереди.

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

iphone - нормально ли использовать usleep на вторичном потоке в Grand Central Dispatch?

Ответ 1

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

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

Но я не вижу проблем с использованием сна, поскольку он, по крайней мере, даст другим потокам/приложениям шанс запустить.

Ответ 2

С тех пор, как этот вопрос вызвал у меня много горя на протяжении многих лет, я решил, что поделился бы опытом этого страдания:

Каждый раз, когда вы блокируете (который включает вызов sleep) в рабочем модуле *, переданном в GCD, вы создаете ситуацию с потенциальным голосом. Хуже того, если блокировка вашей рабочей единицы связана с использованием примитивов синхронизации (то есть семафоров, блокировок и т.д.) Или если несколько рабочих блоков блокируются с использованием этих примитивов, то голодание потока может вызвать взаимоблокировки. Больше в минуту, но здесь короткая версия:

Если вы блокируете рабочие единицы, переданные в GCD:

  • В лучшем случае вы используете GCD неэффективно.
  • В худшем случае вы подвергаете себя голодание и, следовательно, (потенциально) взаимоблокировки.

Вот почему: ОС имеет ограничение на поток для каждого процесса. Изменение предела потока для каждого процесса не является невозможным, но на практике это редко стоит того. У GCD есть собственный предел ширины очереди (количество потоков, которое будет использоваться для обслуживания параллельной очереди). (Хотя детали сложны, стоит отметить, что создание нескольких параллельных очередей не будет, вообще говоря, обойти этот предел.) По определению предел GCD ниже, чем предел для каждого процесса. Кроме того, ограничение ширины очереди GCD является недокументированной деталью реализации. Эмпирически, на момент написания этой статьи, в OS X, я заметил, что ограничение составляет 64. Поскольку этот предел является недокументированной реализацией, вы не можете рассчитывать на то, что это такое. (Также невозможно изменить его с помощью общедоступного API.) В идеале ваш код должен быть написан таким образом, чтобы он все равно выполнялся до завершения, хотя и медленно, если бы GCD изменил ограничение ширины очереди на 1. (Это также может быть утверждал, что то же самое должно быть правдой, даже если этот поток также является основным потоком, но это делает задачу излишне сложной, и кажется безопасным предположить, что всегда будет хотя бы один фоновый поток, поскольку вряд ли стоит использовать GCD, если бы не было.)

Как вы это сделаете? Если вы блокируете время ожидания ввода-вывода, вам нужно рассмотреть возможность перехода вашего кода на использование семейства вызовов dispatch_io. Для ситуации ожидания в квазизависимости (т.е. Исходного вопроса) вы можете использовать dispatch_after, чтобы проверить что-то через заданное количество времени. В других случаях могут потребоваться таймеры отправки или источники отправки.

Я буду первым, кто признает, что не всегда практично (не говоря уже о целесообразности) полностью избегать блокировки в рабочих единицах, представленных в GCD. Кроме того, комбинация блочного синтаксиса GCD и Objective-C позволяет очень просто писать выразительный, легко читаемый код, чтобы избежать ситуаций, когда вы блокировали бы поток пользовательского интерфейса, перемещая блокирующие/синхронные операции в фоновый поток. Что касается ускорения разработки, эта схема чрезвычайно полезна, и я использую ее все время. Тем не менее, стоит знать, что enqueuing рабочей единицы с GCD, который блокирует потребление некоторого количества конечного ресурса (то есть количество доступных потоков), размер которого вы, педантично говоря, не можете знать (потому что это недокументированная деталь реализации и может изменение в любое время) и, фактически, не может управлять (потому что вы не можете установить/изменить ширину очереди GCD с помощью общедоступного API.)

В отношении исходного вопроса: ожидание в ожидании (например, при вызове sleep или usleep) является одним из наиболее предотвратимых и наименее защищенных способов блокировки в рабочем блоке, переданном в GCD. Я пойду дальше и сделаю смелое выражение о том, что всегда лучше, если он менее оперативен для разработки, способ реализовать любую операцию, которая может быть реализована оживленным ожиданием в рабочем блоке GCD.

* Я использую "рабочий блок" для ссылки на Objective-C блоки или указатели/аргументы функций, представленные в GCD для исполнения, чтобы ограничить путаницу работой "блокировки", под которой я подразумеваю "выполнение чего-то, что приводит к тому, что ваш поток приостанавливается в ядре".

Ответ 3

Использование сна с Grand Central Dispatch может быть проблемой, потому что потоки потоков GCD и поэтому вы поддерживаете поток от использования другим заданием. GCD может создать больше потоков, но персонал, которого я избежал бы спать в этой ситуации, будет зависеть от ситуации.