Вот: Dispatch Queues (развитие темы).
Если вкратце, то получается, что приложению доступно три типа очередей.
Serial-очереди (они же private dispatch queues) создаются по мере необходимости самим приложением. Serial-очередь выполняет поставленные в нее задачи в том порядке, в котором задачи были помещены в нее. Serial-очередь выполняет только одну задачу за один раз. Поэтому Serial-очереди рекомендуют использовать для организации доступа к одному разделяемому ресурсу.
Concurrent-очереди (они же global dispatch queues) создаются самой ОС для приложения. Они способны выполнять сразу несколько задач из очереди, но на выполнение задачи поступают в том же порядке, в котором ставились в очередь. ОС предоставляет приложению три таких очереди – с малым, нормальным и высоким приоритетом.
Main-dispatch-очередь. Это очередь, которая так же создается самой ОС и связывается с главной нитью приложения.
В очереди ставятся задачи. Каждая задача оформляется в виде блока кода. По сути – лямбда-функции. Для поддержки которых Apple расширила С-шные языки: C, Objective-C, C++ (если не ошибаюсь, раньше этого не было на Mac-ах):
dispatch_queue_t myCustomQueue;
myCustomQueue =
dispatch_queue_create("com.example.MyCustomQueue", NULL);
dispatch_async(myCustomQueue, ^{
printf("Do some work here.\n");
});
В данном примере блок кода содержится в конструкции ^{…}.
Блоки кода могут ссылаться на переменные, которые находятся вне блока (т.е. лексические замыкания). Те переменные, на которые идут ссылки из блока кода, транслятор оформляет в специальную, размещенную в хипе структуру. Что позволяет обращаться к ним даже после выхода из области видимости, в которой блок был создан. Для примитивных типов, вроде int, это совершенно безопасно. Для сложных типов или динамически-выделяемых ресурсов нужно соблюдать осторожность, чтобы не получить “повисшую” ссылку.
Задачи в очереди можно ставить как в асинхронном режиме (см. dispatch_async выше), так и в синхронном. Постановка задачи в синхронном режиме с помощью dispatch_sync означает, что возврат из dispatch_sync будет выполнен только после завершения обработки задачи. Но здесь важно следить, чтобы dispatch_sync не поставил заявку в ту же очередь, которая обслуживает текущий контекст исполнения – это гарантированно приведет к тупикам.
Еще там есть группы очередей (dispatch groups), которые позволяют объединять ряд очередей в одну группу и дожидаться их общего завершения.
Есть специальные семафоры (dispatch semaphores), которые позволяют ограничивать количество задач, которые могут быть одновременно запущены для обработки какого-то ресурса (пример в документации был не очень выразителен, поэтому я не полностью просек эту фичу).
Есть даже специальная функция для организации параллельной обработки циклов. Т.е. вместо:
for(int i = 0; i < 100; ++i ) {
// Какая-то независимая обработка...
}
можно использовать dispatch_apply:
dispatch_apply(100, queue, ^(size_t i) {
// Какая-то независимая обработка...
});
Вот такой вот лисапед с квадратными колесами. Имхо, подобную штуку можно замутить и на C++0x с лямбда-функциями. А так получилась своего рода “игла для разработчика” – написанный для MacOS код уже никуда не получится портировать, т.к. в коде будут использованы нестандартные расширения языка.
Да, чуть не забыл – под iPhone этот GCD пока не работает.
Комментариев нет:
Отправить комментарий