Как я могу проверить, что я запущен в заданной очереди GCD без использования dispatch_get_current_queue()?

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

Для этого я использовал следующий код:

void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
{
    dispatch_queue_t videoProcessingQueue = [GPUImageOpenGLESContext sharedOpenGLESQueue];

    if (dispatch_get_current_queue() == videoProcessingQueue)
    {
        block();
    }
    else
    {
        dispatch_sync(videoProcessingQueue, block);
    }
}

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

Я слышал противоречивые вещи о том, правильно ли использовать dispatch_get_current_queue() для выполнения подобных сравнений, и я вижу эту формулировку в заголовках:

Рекомендуется только для отладки и ведения журнала:

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

Кроме того, в iOS 6.0 (но еще не для Mountain Lion) заголовки GCD теперь отмечают эту функцию как устаревшую.

Похоже, я не должен использовать эту функцию таким образом, но я не уверен, что я должен использовать на своем месте. Для функции, подобной приведенной выше, ориентированной на основную очередь, я мог бы использовать [NSThread isMainThread], но как я могу проверить, запущен ли я в одной из моих пользовательских последовательных очередей, чтобы предотвратить блокировку?

Ответ 1

Назначьте любой идентификатор, который вы хотите, с помощью dispatch_queue_set_specific(). Затем вы можете проверить свой идентификатор с помощью dispatch_get_specific().

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

Ответ 2

Это очень простое решение. Это не так сильно, как использование dispatch_queue_set_specific и dispatch_get_specific вручную - у меня нет показателей.

#import <libkern/OSAtomic.h>

BOOL dispatch_is_on_queue(dispatch_queue_t queue)
{
    int key;
    static int32_t incrementer;
    CFNumberRef value = CFBridgingRetain(@(OSAtomicIncrement32(&incrementer)));
    dispatch_queue_set_specific(queue, &key, value, nil);
    BOOL result = dispatch_get_specific(&key) == value;
    dispatch_queue_set_specific(queue, &key, nil, nil);
    CFRelease(value);
    return result;
}