Автор (ы) | Дэйв Заржицки |
---|---|
Разработчик (и) | Apple Inc. |
Операционная система | Mac OS X 10.6 (2009) и новее, iOS 4.0 и новее, watchOS, tvOS, FreeBSD |
Тип | Системная утилита |
Лицензия | Apache 2.0 |
Веб-сайт | https://apple.github.io/swift-corelibs-libdispatch/ |
Grand Central Dispatch (GCD или libdispatch ) - это технология, разработанная Apple Inc. для оптимизации поддержки приложений для систем с многоядерными процессорами и другими симметричной многопроцессорной обработкой системы. Это реализация параллелизма задач на основе шаблона пула потоков . Основная идея состоит в том, чтобы перенести управление пулом потоков из рук разработчика в сторону операционной системы. Разработчик вводит в пул «рабочие пакеты», не обращая внимания на архитектуру пула. Эта модель улучшает простоту, портативность и производительность.
GCD впервые был выпущен с Mac OS X 10.6, а также доступен с iOS 4 и выше. Название «Grand Central Dispatch» является отсылкой к Grand Central Terminal.
. Исходный код библиотеки libdispatch, обеспечивающей реализацию служб GCD, был выпущен Apple по лицензии Apache License 10 сентября 2009 г. Он был перенесен на FreeBSD 8.1+, MidnightBSD 0.3+, Linux и Solaris. Попытки в 2011 году заставить libdispatch работать в Windows не были объединены в апстрим. У Apple есть собственный порт libdispatch.dll для Windows, поставляемый с Safari и iTunes, но SDK не предоставляется.
Примерно с 2017 года исходный репозиторий libdispatch, размещенный Ником Хатчинсоном, устарел в пользу версии, которая является частью основной библиотеки Swift, созданной в июне 2016 года. Новая версия поддерживает больше платформ., особенно в том числе Windows.
GCD работает, позволяя ставить в очередь для выполнения определенные задачи в программе, которая может выполняться параллельно, и, в зависимости от доступности ресурсов обработки, планировать их для выполнения на любом из доступных ядер процессора (Apple называет это «маршрутизацией»).
Задача может быть выражена либо как функция, либо как «блок. " Блоки являются расширением синтаксиса языков программирования C, C ++ и Objective-C, которые инкапсулируют код и данные в единый объект аналогично закрытию . GCD по-прежнему можно использовать в средах, где блоки недоступны.
Grand Central Dispatch по-прежнему использует потоки на низком уровне, но отвлекает их от программиста, которому не нужно беспокоиться о стольких деталях. Задачи в GCD легко создавать и ставить в очередь; Apple заявляет, что для постановки рабочего блока в очередь в GCD требуется 15 инструкций, в то время как для создания традиционного потока может потребоваться несколько сотен инструкций.
Задача в Grand Central Dispatch может использоваться либо для создания рабочего элемента, который помещается в очередь или назначается источнику события. Если задача назначена источнику события, то при срабатывании события из блока или функции создается рабочая единица, и эта единица работы помещается в соответствующую очередь. Apple описывает это как более эффективное, чем создание потока, единственной целью которого является ожидание срабатывания одного события.
Среда диспетчеризации объявляет несколько типов данных и функций для их создания и управления ими:
Libdispatch поставляется с собственной объектной моделью OS Object, которая частично совместима с моделью Objective-C. В результате его объекты могут быть бесплатно связаны с объектами ObjC.
Два примера, демонстрирующие использование Grand Central Dispatch, можно найти в Ars Technica Джона Сиракузы. Обзор снежного барса. Первоначально приложение на основе документа имеет метод под названием analysisDocument
, который может делать что-то вроде подсчета количества слов и абзацев в документе. Обычно это быстрый процесс, который может выполняться в основном потоке, и пользователь не замечает задержки между нажатием кнопки и отображением результатов.
- (IBAction) analysisDocument: (NSButton *) отправитель {NSDictionary * stats = [myDoc analysis]; [myModel setDict: статистика]; [myStatsView setNeedsDisplay: ДА]; }
Если документ большой и для выполнения анализа требуется много времени, тогда основной поток будет ждать завершения функции. Если это займет достаточно времени, пользователь заметит, и приложение может даже «beachball ». Решение можно увидеть здесь:
- (IBAction) analyseDocument: (NSButton *) sender {dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {NSDictionary * stats = [myDoc analysis]; ^ dispatch_asqueget_mainc [myModel setDict: stats]; [myStatsView setNeedsDisplay: YES];});}); }
Здесь вызов [myDoc analysis]
помещается в блок , который затем помещается в одну из глобальных параллельных очередей. После завершения выполнения [myDoc analysis]
новый блок помещается в основную очередь (в которой выполняется основной поток приложения), который обновляет GUI (это необходимо, потому что графический интерфейс может обновляться только основным потоком). Внеся эти два небольших изменения, разработчик избежал потенциальной остановки приложения, которую видит пользователь, и позволил своему приложению лучше использовать аппаратные ресурсы.
Второй пример - это пример распараллеливания цикла for:
for (i = 0; i < count; i++) { results[i] = do_work(data, i); } total = summarize(results, count);
Этот код запускает функцию do_work
count
раз, присваивая результат i th элементу i th в массиве results
, а затем вызывает summarize в массиве после завершения цикла. К сожалению, работа вычисляется последовательно там, где это может не быть. Предполагая, что do_work не полагается на результаты каких-либо других обращений к нему, нет причин, по которым эти вызовы не могут выполняться одновременно. Вот как это будет сделано в GCD:
dispatch_apply (count, dispatch_get_global_queue (0, 0), ^ (size_t i) {results [i] = do_work (data, i);}); total = summarize (results, count);
Здесь dispatch_apply
запускает переданный ему блок, count
раз, помещая каждый вызов в глобальную очередь и передавая каждому вызову блока другое число от 0 до count
-1. Это позволит ОС распределять работу по своему усмотрению, выбирая opti малое количество потоков для выполнения при текущей загрузке оборудования и системы. dispatch_apply
не возвращается до тех пор, пока все блоки, которые он помещает в данную очередь, не завершат выполнение, так что можно гарантировать, что вся работа внутри исходного цикла завершена до вызова суммировать
.
Программисты могут создавать свои собственные последовательные очереди для задач, которые, как они знают, должны выполняться последовательно, но которые могут выполняться в отдельном потоке. Новая очередь будет создана следующим образом:
dispatch_queue_t exampleQueue; exampleQueue = dispatch_queue_create ("com.example.unique.identifier", NULL); // Здесь можно использовать exampleQueue. dispatch_release (exampleQueue);
Следует проявлять осторожность, чтобы не допустить, чтобы отправленный блок в очереди синхронно помещал другой блок в ту же очередь, поскольку это гарантированно приведет к взаимоблокировке. Такой код может выполнять следующие действия:
dispatch_queue_t exampleQueue = dispatch_queue_create ("com.example.unique.identifier", NULL); dispatch_sync (exampleQueue, ^ {dispatch_sync (exampleQueue, ^ {printf ("Теперь я зашел в тупик... \ n");});}); dispatch_release (exampleQueue);
GCD используется во всей macOS (начиная с 10.6 Snow Leopard), и Apple поощряет его внедрение разработчиками приложений для macOS. FreeBSD разработчик Роберт Уотсон объявил о первой адаптации крупного приложения с открытым исходным кодом, Apache HTTP Server, для использования GCD через Apache GCD MPM (Multi-Processing Module) 11 мая 2010 г., чтобы проиллюстрировать модель программирования и способы интеграции GCD в существующие крупномасштабные многопоточные приложения. В его заявлении было отмечено, что GCD MPM имеет от одной трети до половины количества строк, чем другие многопоточные MPM.
GCD реализуется libdispatch с поддержкой расширений pthreads, не относящихся к POSIX, разработанных Яблоко. Apple изменила интерфейс с момента своего создания (в OS X 10.5) посредством официального запуска GCD (10.6), Mountain Lion (10.8) и недавно Mavericks (10.9). Последние изменения заключаются в том, чтобы сделать код, поддерживающий потоки pthread, как в пользовательском режиме, так и в ядре, частным (при этом поддержка pthread в ядре ограничена только оболочками, а фактическая реализация очереди перемещена в отдельное расширение ядра).
В других системах, libdispatch реализует свою собственную очередь работ, используя собственные средства обработки событий системы (epoll, kevent или Windows NT). В macOS kevent используется с рабочей очередью ядра.