Использование Grand Central Dispatch вне приложения или цикла выполнения

В документации GCD совершенно ясно, что для отправки работы в основную очередь вам нужно либо работать в NSApplication (или UIApplication), либо вызывать dispatch_main(), чтобы действовать как своего рода цикл выполнения. Однако нужно ли мне что-то делать для настройки глобальной параллельной очереди?

По сути, я спрашиваю: если я пишу простую программу на C, нужно ли мне выполнять какие-либо специальные настройки, прежде чем я вызову dispatch_get_global_queue() и начну работать?


person dented42    schedule 03.12.2011    source источник


Ответы (2)


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

Пример минимальной программы на C, использующей GCD и глобальную очередь (на основе http://wiki.freebsd.org/GCD ):

#include <dispatch/dispatch.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC);
    dispatch_after(dispatchTime, globalQueue, ^{
        printf("Dispatched on global queue\n");
        exit(0);
    });
    dispatch_main();
    return (0);
}

Чтобы скомпилировать это, используйте:

clang -Wall -Werror -fblocks -L/usr/local/lib -I/usr/local/include -o test test.c
person Thomas Zoechling    schedule 03.12.2011
comment
Вам не нужен вызов dispatch_main. Если вы замените его на 10-секундный сон, например, этот 5-секундный таймер все равно будет срабатывать. - person Stripes; 03.12.2011

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

int main() {
    __block int count = 10;
    dispatch_semaphore_t done = dispatch_semaphore_create(0);
    dispatch_time_t naptime;
    
    // timeout after 5 seconds
    naptime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)5E9);
    // no timeout
    //naptime = dispatch_time(DISPATCH_TIME_FOREVER, 0);
    
    // schedule some work
    dispatch_async(
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
        ^{
            dispatch_apply(count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0),
                ^(size_t i){
                    //...
                    // note: potential race condition on count.
                    // Synchronization left as an exercise.
                    if (--count == 0) {
                        dispatch_semaphore_signal(done);
                    }
                }
            );
        }
    );
    
    if (dispatch_semaphore_wait(done, naptime)) {
        // processing didn't complete in allotted time
        //...
    }
dispatch_release(done);
    return 0;
}

Вместо семафоров есть концептуально более простой, но менее полезный подход вызова sleep, или подсчета до огромного числа в цикле (убедитесь, что компилятор не оптимизирует его), или цикла до переменной (изначально установленной в false, установите значение true после завершения обработки) имеет значение true (известное как ожидание занятости). Каждый из них имеет серьезные недостатки и гораздо менее предпочтителен, чем семафор.

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

Есть веские причины для вызова dispatch_main или запуска цикла выполнения, но имейте в виду, что то, что вы отправляете в любую очередь, кроме основной очереди, может начаться ДО запуска цикла выполнения для dispatch_main.

person Stripes    schedule 03.12.2011
comment
Это также хороший ответ на вопрос Как изящно выйти из программы на основе libdispatch. Отмечено для дальнейшего использования. - person eonil; 16.10.2013
comment
Это требует ручного подсчета «заданий в очереди». Есть ли способ завершить dispatch_main(), когда все очереди опустошены/и им не назначены параметры dispatch_source? Подобно NSRunLoop (который, насколько мне известно, просто завершает работу, если запускать больше нечего). - person hnh; 17.06.2015
comment
Нет, самое близкое, что я могу придумать, это использовать все dispatch_group_async для начала работы, а затем использовать dispatch_group_notify для запуска выхода, но это не является общим или автоматическим. Может быть, что-то с началом всей работы, добавляющей обработчики Dealloc в очереди и освобождающей их, но это имеет проблемы с незаконным воскрешением. Я думаю, что ваше приложение по своей сути должно знать, как распознавать полноту, диспетчеризация может помочь распространить это сообщение, но не может его создать. - person Stripes; 24.06.2015