Получить текущую очередь отправки?

У меня есть метод, который должен поддерживать вызов из любой очереди и должен ожидать. Он запускает некоторый код в фоновом потоке сам, а затем использует dispatch_get_main_queue, когда он возвращает значение для аргумента block. Я не хочу, чтобы он заставлял его на основную очередь, если это не было, когда он ввел метод. Есть ли способ получить указатель на текущую очередь отправки?

Ответ 1

У вас есть опция dispatch_get_current_queue(), однако SDK для iOS 6.1 определяет этот API со следующими оговорками:

" Recommended for debugging and logging purposes only: "

а также

This function is deprecated and will be removed in a future release. " This function is deprecated and will be removed in a future release. "

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

Ответ 2

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

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

Если просто узнать, есть ли вызывающий объект в основном потоке или нет, вы можете использовать +[NSThread isMainThread], чтобы узнать. В общем случае все блоки, исполняемые в главной очереди GCD, будут выполняться в основном потоке. (Одно исключение из этого правила заключается в том, что если ваше приложение использует dispatch_main() вместо основного цикла запуска, вам нужно будет использовать dispatch_get_specific и друзей для уверенности в том, что вы выполняете в главной очереди - это сравнительно редкое обстоятельство.) Чаще всего обратите внимание, что не весь код, который выполняется в основном потоке, выполняется в главной очереди через GCD; GCD подчинен основной линии runloop. Для вашего конкретного случая кажется, что этого может быть достаточно.

Ответ 3

Если вы работаете с NSOperationQueue, он может предоставить вам текущую очередь отправки.

NSOperationQueue имеет функцию класса [NSOperationQueue currentQueue], которая возвращает текущую очередь в виде объекта NSOperationQueue. Чтобы получить объект очереди отправки, вы можете использовать [NSOperationQueue currentQueue].underlyingQueue, который возвращает вашу текущую очередь в виде dispatch_queue_t.

Свифт 3:

if let currentDispatch = OperationQueue.current?.underlyingQueue {
    print(currentDispatch)
}

- работает для основной очереди!

Ответ 4

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

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

let isOnMainQueue = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))

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

com.apple.root.user-interactive-qos //qos_class_t(rawValue: 33)
com.apple.root.user-initiated-qos   //qos_class_t(rawValue: 25)
com.apple.root.default-qos          //qos_class_t(rawValue: 21)  
com.apple.root.utility-qos          //qos_class_t(rawValue: 17)
com.apple.root.background-qos       //qos_class_t(rawValue: 9) 

И затем вы можете использовать dispatch_get_global_queue(qos_class_self(), 0), который вернет вам ту же самую глобальную очередь, в которой вы работаете.

Но я считаю, что Apple особенно препятствует нам ограничивать логику в очередь, на которую нас вызвали, поэтому лучше использовать это для исключительно задач отладки.

Ответ 5

На самом деле есть способ сравнить очередь.

Когда вы настраиваете свою очередь, убедитесь, что вы добавили ярлык. Для моих целей у меня есть общая очередь, которая используется для доступа к базе данных для предотвращения блокировки базы данных. В моем файле DB.m я определил функцию общей очереди, например:

const char *kTransactionQueueLabel = "DB_TRANSACTION_DISPATCH_QUEUE";

+ (dispatch_queue_t)sharedDBTransactionQueue {
    static dispatch_queue_t sharedDBQueue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedDBQueue = dispatch_queue_create(kTransactionQueueLabel, DISPATCH_QUEUE_SERIAL);
    });

    return sharedDBQueue;
}

Очередь транзакций shared db используется локально в файле для отправки всех исполнений в базу данных. Тем не менее, для этого есть и общедоступный аксессуар, который позволяет отправлять целые транзакции в базу данных. Поэтому внутренне, если из очереди транзакций вызывается метод доступа к БД, нам нужно отправлять внутренне в другую очередь (все синхронные рассылки). Поэтому я всегда отправляю нужную очередь, используя нижепользователь.

/**
 * @description Decide which queue to use - if we are already in a transaction, use the internal access queue, otherwise use the shared transaction queue.
 */
- (dispatch_queue_t)getProperQueueForExecution {
    const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
    dispatch_queue_t sharedAccessQueue = [DB sharedDBTransactionQueue];
    if (strcmp(currentLabel, kTransactionQueueLabel) == 0) {
        sharedAccessQueue = [DB sharedInternalDBAccessQueue];
    }

    return sharedAccessQueue;
}

Надеюсь, это поможет. Извините за длинный пример. Суть в том, что вы можете использовать

const char *currentLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);

чтобы получить метку текущей очереди и сравнить с определенной меткой.

Ответ 6

В качестве альтернативного подхода к этому методу NSOBject performSelector:withObject:afterDelay: отправляет вызов в текущий цикл выполнения потока. Согласно документам:

Этот метод устанавливает таймер для выполнения сообщения aSelector на текущий цикл выполнения потоков.

Очевидно, я предлагаю использовать это с задержкой нуля, которая, согласно документам снова:

Задание задержки 0 не обязательно приводит к тому, что селектор будет выполняется немедленно. Селектор по-прежнему поставлен в очередь на потоки и выполнить как можно скорее.

К сожалению, для этого требуется ровно один аргумент, поэтому некоторые методы могут понадобиться, если ваш метод принимает больше или меньше.

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

PS: Существуют два разных NSOBject, протокол и реализация. Уведомление NSOBject:

@interface NSObject <NSObject> { ... }

Может показаться странным, но объявляется (после @interface), а другой - ранее объявленным протоколом (между < и >). При объявлении протокола, который расширяет NSObject (т.е., @protocol Foo <NSObject>), протокол наследует методы от более поздней, но не первой. В конечном итоге протокол реализуется некоторым классом, который наследует реализацию NSOBject, поэтому все экземпляры, наследующие от реализации NSOBject, все еще сохраняются. Но я ухожу от темы.

Ответ 7

Основано на источнике из SQLite.swift.
Если вы хотите проверить, находитесь ли вы в собственной специальной очереди отправки:

class Worker {
    private static let queueKey = DispatchSpecificKey<Int>()
    private lazy var queueContext = unsafeBitCast(self, to: Int.self)
    private lazy var queue: DispatchQueue = {
        let value = DispatchQueue(label: "com.example.App.Worker")
        value.setSpecific(key: Worker.queueKey, value: queueContext)
        return value
    }()

    func test(x: Int) -> Int {
        return dispatchSync {
            return x > 2 ? test(x: x - 1) * x : x
        }
    }

    private func dispatchSync<T>(_ block: () throws -> T) rethrows -> T {
        if DispatchQueue.getSpecific(key: Worker.queueKey) != queueContext {
            return try queue.sync(execute: block)
        }
        return try block()
    }
}

let worker = Worker()
worker.test(x: 5)

Ответ 8

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

// cache value for if we should callback on main queue
BOOL callbackOnMT = [NSThread isMainThread];

// ...
// ... do async work...
// ...

if (callbackOnMT && ![NSThread isMainThread]){
    dispatch_async(dispatch_get_main_queue(), ^{
        // callback to user on main queue
        // as they called this function on main queue
        callbackToUser();
    });
}
else{
    // callback to user on our current queue
    // as they called this function on a non-main queue
    callbackToUser();
}

Ответ 9

Чтобы получить метку текущей очереди и сравнить с определенной меткой, используйте.

let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)