获取当前调度队列?

Posted

技术标签:

【中文标题】获取当前调度队列?【英文标题】:Get current dispatch queue? 【发布时间】:2013-07-02 17:41:58 【问题描述】:

我有一个方法应该支持从任何队列中被调用,并且应该可以。

它在后台线程本身中运行一些代码,然后在向其块参数返回值时使用dispatch_get_main_queue

如果它不是在它进入方法时,我不希望它强制它进入主队列。有没有办法获得指向当前调度队列的指针?

【问题讨论】:

对于 Swift,请查看***.com/a/54869129/308315 forums.swift.org/t/… 【参考方案1】:

如果您使用的是NSOperationQueue,它可以为您提供当前的调度队列。

NSOperationQueue 具有类函数[NSOperationQueue currentQueue],它将当前队列作为 NSOperationQueue 对象返回。要获取调度队列对象,您可以使用[NSOperationQueue currentQueue].underlyingQueue,它将您当前的队列作为dispatch_queue_t 返回。

斯威夫特 3:

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

- 适用于主队列!

【讨论】:

请注意,underlyingQueue 是在 ios 8.0 中添加的。 我一直在玩这个,如果您的代码在与 @ 无关的 GCD 队列中执行,您似乎不会得到 [NSOperationQueue currentQueue] 的正确返回值987654327@。换句话说,如果我直接在 GCD 队列中执行一个块并从该块中调用[NSOperationQueue currentQueue].underlyingQueue,我永远不会得到与我正在执行该块的实际队列相同的值。 我认为@emaloney 是对的。我做了一个实验,使用dispatch_sync(myqueue, ^) 运行 gcd 任务块,[NSOperationQueue currentQueue] 返回主队列。 这个答案应该更新,正如@emaloney所说。 OperationQueue.current?.underlyingQueue 应该只在正确的上下文中使用。 这个答案具有误导性,因为它看起来应该适用于任何情况......它不是。它仅适用于您使用OperationQueue 时,如果您直接使用DispatchQueue 则不适用。请更新您的答案以表明这一点。【参考方案2】:

随着dispatch_get_current_queue() 的弃用,实际上无法知道您正在执行哪个队列。如果您仔细阅读GCD sources,您最终会发现这是因为“我在哪个队列上执行?”这个问题可能有多个答案。 (因为队列最终以全局队列之一为目标,等等)

如果您想保证未来块在特定队列上运行,那么唯一的方法是让您的 API 接受队列作为参数以及完成块。这让调用者可以决定在哪里执行完成。

如果只知道调用者是否在主线程上就足够了,你可以使用+[NSThread isMainThread] 来查找。在一般情况下,在主 GCD 队列上执行的所有块都将在主线程上执行。 (此规则的一个例外是,如果您的应用程序使用 dispatch_main() 代替主运行循环,您将必须使用 dispatch_get_specific 和朋友来确定您正在主队列上执行 - 这是一个相对罕见的情况。)更常见的是,请注意并非所有在主线程上执行的代码都通过 GCD 在主队列上执行; GCD 从属于主线程 runloop。对于您的具体情况,这听起来可能就足够了。

【讨论】:

【参考方案3】:

您可以选择 "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.”。

Here's another related question with some alternatives 您可以考虑是否需要面向未来的代码。

【讨论】:

这已被弃用,不应删除。替换为dispatchPrecondition【参考方案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 尤其不鼓励我们将逻辑限制在我们被调用的队列中,因此最好将其用于专门的调试目的。

【讨论】:

我发现这种方法不可靠。根据文档,label是可选参数,所以可以为NULL。如果在创建时没有提供标签,则 dispatch_queue_get_label() 返回一个空字符串。 没错,这只是一种解决方法,正如我所说的 - 对于测试和调试它可能特别有用,但在代码中绑定逻辑并不是一个好主意.. 由于解决方法似乎是最知名的方法,我必须使用它来确定当前队列是否是特定的串行队列。如果是,直接调用blocks而不是调用dispatch_sync导致死锁。为了避免我之前提到的极端情况。我只是简单地拒绝任何没有标签的队列。 通常我会尽量避免使用 dispatch_sync。我通常设计一切异步,然后在前一个调度队列上回调。我认为从设计的角度来看,通常最好不要在运行时确定它应该在哪个队列上执行。拥有基于其特定单一目的的队列通常是我用来设计的方式,所以我永远不需要检查我正在运行哪个队列。 值得一提的是,在 Swift 4 中,这种方法不可用,但是通过一些可怕的工作,您仍然可以获得标签 extension DispatchQueue class var currentLabel: String return String(validatingUTF8: __dispatch_queue_get_label(nil)) ?? "unknown" 【参考方案5】:

基于Oleg Barinov回答

详情

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

【讨论】:

您的解决方案目前有效!谢谢。如果我遇到任何值得一提的事情会更新。【参考方案6】:

基于来自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)

【讨论】:

【参考方案7】:

作为NSOBject 的方法performSelector:withObject:afterDelay: 的替代方法,在当前线程的运行循环上调度调用。根据文档:

这个方法设置一个定时器来执行 aSelector 消息 当前线程的运行循环。

显然,我建议以零延迟使用它,再次根据文档:

指定延迟为 0 并不一定会导致选择器 立即执行。选择器仍然在线程的队列中 运行循环并尽快执行。

不幸的是,它只需要一个参数,因此如果您的方法需要更多或更少,则可能需要一些解决方法。

我注意到的另一件事是,此方法不适用于协议,而仅适用于实现。这是由于此方法存在于NSObject 类别中,而不是在NSObject 接口中(参见下面的PS)。这可以通过转换为 id 轻松解决。

PS:存在两个不同的NSObjects,一个协议和一个实现。通知NSObject声明:

@interface NSObject <NSObject>  ... 

这可能看起来很奇怪,但一个正在声明(在@interface 之后),另一个是之前声明的协议(在&lt;&gt; 之间)。当声明一个扩展 NSObject 的协议(即@protocol Foo &lt;NSObject&gt;)时,该协议继承了后者的方法,而不是前者。最终,该协议由继承自NSObject 实现的某个类实现,因此从NSObject 实现继承的所有实例仍然成立。但我跑题了。

【讨论】:

或者您可以使用self.perform(#selector(myfunc), on: Thread.current, with: nil, waitUntilDone: false) 在队列的同一线程上执行 myfunc。【参考方案8】:

其实还是有办法比较队列的。

设置队列时,请确保添加标签。出于我的目的,我有一个共享队列,用于访问数据库以防止数据库锁定。在我的 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;

共享数据库事务队列在文件中本地使用,以将所有执行分派到数据库。但是,还有一个公共访问器允许将整个事务分派到数据库。所以在内部,如果从事务队列中调用 DB 访问方法,我们需要在内部调度不同的队列(所有同步调度)。所以在内部,我总是使用下面的 getter 在正确的队列上调度。

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

获取当前队列的标签并与定义的标签进行比较。

【讨论】:

【参考方案9】:

如果您只对当前 QOS 感兴趣,请检查 Thread.current.qualityOfService 的值。

【讨论】:

【参考方案10】:

我有与原始帖子提到的相同的功能要求。您应该能够在任何队列上调用此异步函数,但如果在主队列上调用,则在主队列上回调给用户。我只是这样处理它:

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

【讨论】:

【参考方案11】:

获取当前队列的标签并使用已定义的标签进行比较。

let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)

【讨论】:

以上是关于获取当前调度队列?的主要内容,如果未能解决你的问题,请参考以下文章

1.3. 调度结构:运行队列调度类调度组调度域

1.3. 调度结构:运行队列调度类调度组调度域

在 Objective-c 中使用调度队列

C#怎么从RabbitMQ中获取当前所有队列

进程调度算法

程序一直处于Accept状态,无法调度运行