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

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

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

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


person Andrew    schedule 04.07.2013    source источник
comment
Для Swift ознакомьтесь с stackoverflow.com/a/54869129/308315   -  person iwasrobbed    schedule 25.02.2019
comment
forum.swift.org/t/   -  person pkamb    schedule 05.11.2019


Ответы (11)


У вас есть возможность "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.".

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

person Michael Dautermann    schedule 04.07.2013
comment
Это устарело, и его не следует удалять. Замена будет dispatchPrecondition. - person eonil; 09.07.2020

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

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

Swift 3:

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

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

person Palle    schedule 17.04.2015
comment
Обратите внимание, что основная очередь была добавлена ​​в iOS 8.0. - person Ben Flynn; 04.05.2015
comment
Я играл с этим, и не похоже, что вы получите правильное возвращаемое значение для [NSOperationQueue currentQueue], если ваш код выполняется в очереди GCD, не связанной с NSOperationQueue. Другими словами, если я выполняю блок в очереди GCD напрямую и вызываю [NSOperationQueue currentQueue].underlyingQueue из этого блока, я никогда не получу того же значения, что и фактическая очередь, в которой я выполняю блок. - person emaloney; 16.05.2015
comment
Я думаю, что @emaloney права. Я провел эксперимент, который запускал блок задач gcd с dispatch_sync(myqueue, ^{}) и [NSOperationQueue currentQueue] возвращал основную очередь. - person xi.lin; 05.05.2016
comment
Этот ответ следует обновить, как сказал @emaloney. OperationQueue.current? .UnderlyingQueue следует использовать только в правильном контексте. - person Dmytro Hutsuliak; 24.01.2018
comment
Этот ответ вводит в заблуждение, поскольку он читается так, как будто он должен применяться в любой ситуации ... это не так. Это применимо только в том случае, если вы используете OperationQueue, что НЕ верно, если вы используете DispatchQueue напрямую. Обновите свой ответ, чтобы указать на это. - person JRG-Developer; 26.07.2019
comment
Документировано, что свойство underlyingQueue не равно нулю, если вы явно назначаете dispatch_queue_t. - person Ortwin Gentz; 22.02.2021

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

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

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

person ipmcc    schedule 16.07.2013

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

person ambientlight    schedule 08.01.2016
comment
Я обнаружил, что этот метод ненадежен. Согласно документу, метка является необязательным параметром, поэтому может иметь значение NULL. dispatch_queue_get_label () возвращает пустую строку, если при создании метка не была предоставлена. - person soflare; 10.03.2016
comment
это правда, это всего лишь обходной путь, как я уже сказал - для тестирования и отладки это может быть особенно полезно, но привязка логики в коде - не лучшая идея .. - person ambientlight; 11.03.2016
comment
Поскольку обходной путь кажется наиболее известным методом, я должен использовать его, чтобы определить, является ли текущая очередь определенной последовательной очередью. Если да, просто вызывайте блоки напрямую вместо вызова dispatch_sync, который вызывает мертвую блокировку. Чтобы избежать углового случая, о котором я упоминал ранее. Я просто отклоняю любую очередь без метки. - person soflare; 11.03.2016
comment
Обычно я стараюсь избегать использования dispatch_sync там, где это возможно. Обычно я проектирую все асинхронно, а затем выполняю обратный вызов в предыдущей очереди отправки. Я думаю, что с точки зрения дизайна часто бывает хорошо, когда во время выполнения не определяется, в какой очереди он должен выполняться. Как правило, я использую при проектировании очередь, основанную на ее конкретном единственном назначении, поэтому мне никогда не нужно проверять, в какой очереди я работаю. - person ambientlight; 11.03.2016
comment
Стоит упомянуть, что в Swift 4 этот метод недоступен, но с помощью ужасной работы вы все равно можете получить метку extension DispatchQueue { class var currentLabel: String { return String(validatingUTF8: __dispatch_queue_get_label(nil)) ?? "unknown" } } - person sliwinski.lukas; 14.06.2018

На основе ответа Олега Баринова

Подробности

  • Swift 5.1, Xcode 11.3.1

Решение

import Foundation

// MARK: private functionality

extension DispatchQueue {

    private struct QueueReference { weak var queue: DispatchQueue? }

    private static let key: DispatchSpecificKey<QueueReference> = {
        let key = DispatchSpecificKey<QueueReference>()
        setupSystemQueuesDetection(key: key)
        return key
    }()

    private static func _registerDetection(of queues: [DispatchQueue], key: DispatchSpecificKey<QueueReference>) {
        queues.forEach { $0.setSpecific(key: key, value: QueueReference(queue: $0)) }
    }

    private static func setupSystemQueuesDetection(key: DispatchSpecificKey<QueueReference>) {
        let queues: [DispatchQueue] = [
                                        .main,
                                        .global(qos: .background),
                                        .global(qos: .default),
                                        .global(qos: .unspecified),
                                        .global(qos: .userInitiated),
                                        .global(qos: .userInteractive),
                                        .global(qos: .utility)
                                    ]
        _registerDetection(of: queues, key: key)
    }
}

// MARK: public functionality

extension DispatchQueue {
    static func registerDetection(of queue: DispatchQueue) {
        _registerDetection(of: [queue], key: key)
    }

    static var currentQueueLabel: String? { current?.label }
    static var current: DispatchQueue? { getSpecific(key: key)?.queue }
}

использование

Обнаружить системную очередь

DispatchQueue.currentQueueLabel
DispatchQueue.current
DispatchQueue.global(qos: .default) == DispatchQueue.current
DispatchQueue.main === DispatchQueue.current

Обнаружить настраиваемую очередь

let queue = DispatchQueue(label: "queue-sample")
DispatchQueue.registerDetection(of: queue)
if DispatchQueue.current == queue { ... }

Образец

Не забудьте вставить сюда код решения.

func subTest(queue: DispatchQueue) {
    queue.async {
        print("--------------------------------------------------------")
        print("queue label: \(DispatchQueue.currentQueueLabel ?? "nil")")
        print("print DispatchQueue.current: \(String(describing: DispatchQueue.current))")
        print("print queue == DispatchQueue.current: \(queue == DispatchQueue.current)")
        print("print queue === DispatchQueue.current: \(queue === DispatchQueue.current)")
        print("DispatchQueue.main == DispatchQueue.current: \(DispatchQueue.main == DispatchQueue.current)\n")
    }
}

func test() {
    subTest(queue: DispatchQueue.main)
    sleep(1)
    subTest(queue: DispatchQueue.global(qos: .default))
    sleep(1)
    subTest(queue: DispatchQueue.global(qos: .utility))
    sleep(1)

    let queue = DispatchQueue(label: "queue-sample")
    DispatchQueue.registerDetection(of: queue)
    subTest(queue: queue)
    sleep(1)
}

test()
DispatchQueue.global(qos: .default).async {
    test()
}

Результат

--------------------------------------------------------
queue label: com.apple.root.default-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.default-qos[0x7fff89eb47c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false

--------------------------------------------------------
queue label: com.apple.root.utility-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.utility-qos[0x7fff89eb46c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false

--------------------------------------------------------
queue label: queue-sample
print DispatchQueue.current: Optional(<OS_dispatch_queue_serial: queue-sample[0x600000275780] = { xref = 7, ref = 3, sref = 2, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x0060002500000b01, enqueued, max qos 5, draining on 0xb03, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false

--------------------------------------------------------
queue label: com.apple.main-thread
print DispatchQueue.current: Optional(<OS_dispatch_queue_main: com.apple.main-thread[0x7fff89eb43c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x001ffe9000000300, dirty, in-flight = 0, thread = 0x303 }>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: true

--------------------------------------------------------
queue label: com.apple.main-thread
print DispatchQueue.current: Optional(<OS_dispatch_queue_main: com.apple.main-thread[0x7fff89eb43c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x001ffe9000000300, dirty, in-flight = 0, thread = 0x303 }>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: true

--------------------------------------------------------
queue label: com.apple.root.default-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.default-qos[0x7fff89eb47c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false

--------------------------------------------------------
queue label: com.apple.root.utility-qos
print DispatchQueue.current: Optional(<OS_dispatch_queue_global: com.apple.root.utility-qos[0x7fff89eb46c0] = { xref = -2147483648, ref = -2147483648, sref = 1, target = [0x0], width = 0xfff, state = 0x0060000000000000, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false

--------------------------------------------------------
queue label: queue-sample
print DispatchQueue.current: Optional(<OS_dispatch_queue_serial: queue-sample[0x60000027a280] = { xref = 7, ref = 3, sref = 2, target = com.apple.root.default-qos.overcommit[0x7fff89eb4840], width = 0x1, state = 0x0060002500000b01, enqueued, max qos 5, draining on 0xb03, in-barrier}>)
print queue == DispatchQueue.current: true
print queue === DispatchQueue.current: true
DispatchQueue.main == DispatchQueue.current: false
person Vasily Bodnarchuk    schedule 20.02.2020
comment
Ваше решение пока работает! Спасибо. Будет обновлено, если я встречу что-нибудь стоящее упоминания. - person elight; 15.04.2020

На основе источника из 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)
person Oleg Barinov    schedule 17.07.2019

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

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

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

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

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

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

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

@interface NSObject <NSObject> { ... }

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

person André Fratelli    schedule 11.03.2016
comment
Или вы можете использовать self.perform(#selector(myfunc), on: Thread.current, with: nil, waitUntilDone: false) для выполнения myfunc в том же потоке вашей очереди. - person iUrii; 01.04.2020

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

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

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

person johnrechd    schedule 08.07.2016

Если вас интересует только текущий QOS, проверьте значение Thread.current.qualityOfService.

person Tamás Sengel    schedule 17.04.2020

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

// 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();
}
person user1687195    schedule 27.11.2017

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

let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)
person vikas kumar jangir    schedule 17.05.2019