it-swarm.com.ru

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

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

40
Andrew

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

"Recommended for debugging and logging purposes only:"

а также

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

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

23
Michael Dautermann

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

Свифт 3: 

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

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

28
Palle

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

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

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

27
ipmcc

С отказом от 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 особенно отговаривает нас от привязки логики к очереди, к которой мы обратились, поэтому лучше использовать ее исключительно в целях отладки.

14
ambientlight

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

Когда вы настраиваете свою очередь, убедитесь, что вы добавили метку. Для моих целей у меня есть общая очередь, которая используется для доступа к базе данных, чтобы предотвратить блокировку базы данных. В моем файле 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;
}

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

/**
 * @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);

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

2
johnrechd

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

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

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

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

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

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

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

@interface NSObject <NSObject> { ... }

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

1
André Fratelli

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

// 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();
}
0
user1687195