Есть ли причина не использовать режим сна в очереди Grand Central Dispatch? - PullRequest
12 голосов
/ 19 июля 2011

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

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

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

Ответы [ 3 ]

10 голосов
/ 16 июля 2013

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

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

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

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

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

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

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

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

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

7 голосов
/ 19 июля 2011

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

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

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

1 голос
/ 19 июля 2011

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

...