У меня есть метод, который должен поддерживать вызов из любой очереди и должен ожидать. Он запускает некоторый код в фоновом потоке сам, а затем использует 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)