簡體   English   中英

獲取當前的調度隊列?

[英]Get current dispatch queue?

我有一個方法應該支持從任何隊列中調用,並且應該支持。

它自己在后台線程中運行一些代碼,然后在它返回一個值給它的塊參數時使用dispatch_get_main_queue

如果它不是在進入方法時,我不希望它強制進入主隊列。 有沒有辦法獲得指向當前調度隊列的指針?

如果您正在使用NSOperationQueue ,它可以為您提供當前的調度隊列。

NSOperationQueue 有類函數[NSOperationQueue currentQueue] ,它將當前隊列作為 NSOperationQueue 對象返回。 要獲取調度隊列對象,您可以使用[NSOperationQueue currentQueue].underlyingQueue ,它將您的當前隊列作為dispatch_queue_t返回。

斯威夫特 3:

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

- 適用於主隊列!

隨着dispatch_get_current_queue()的棄用,實際上無法知道您正在執行哪個隊列。 如果您仔細閱讀GCD 源代碼,您最終會發現這是因為“我在哪個隊列上執行?”這個問題可能有多種答案。 (因為隊列最終以全局隊列之一為目標,等等)

如果您想保證未來塊在特定隊列上運行,那么唯一的方法是讓您的 API 接受隊列作為參數以及完成塊。 這讓調用者決定在何處執行完成。

如果僅僅知道調用者是否在主線程上就足夠了,您可以使用+[NSThread isMainThread]來查找。 在一般情況下,在主 GCD 隊列上執行的所有塊都將在主線程上執行。 (此規則的一個例外是,如果您的應用程序使用dispatch_main()代替主運行循環,您將必須使用dispatch_get_specific和朋友來確定您正在主隊列上執行——這是一種相對罕見的情況.) 更常見的是,注意並非所有在主線程上執行的代碼都通過 GCD 在主隊列上執行; GCD 從屬於主線程 runloop。 對於您的具體情況,這聽起來可能就足夠了。

您確實可以選擇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. "。

這是另一個相關問題,如果您想要面向未來的代碼,您可以考慮一些替代方案

隨着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.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

基於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)

作為此NSOBject方法的替代方法performSelector:withObject:afterDelay:在當前線程的運行循環上調度調用。 根據文檔:

該方法設置了一個計時器來在當前線程的運行循環上執行 aSelector 消息。

顯然,我建議在延遲為零的情況下使用它,根據文檔再次:

指定延遲 0 不一定會導致選擇器立即執行。 選擇器仍然在線程的運行循環中排隊並盡快執行。

不幸的是,它只需要一個參數,因此如果您的方法需要更多或更少的時間,則可能需要一些解決方法。

我注意到的另一件事是這種方法不適用於協議,而只能用於實現。 這是因為這個方法存在於NSObject類別中,而不是在NSObject接口中(請參閱下面的 PS)。 這可以通過強制轉換為id輕松解決。

PS:存在兩個不同的NSObject ,一個協議和一個實現。 注意NSObject聲明:

@interface NSObject <NSObject> { ... }

這可能看起來很奇怪,但一個是被聲明的(在@interface之后),另一個是先前聲明的協議(在<>之間)。 當聲明一個擴展 NSObject 的協議(即@protocol Foo <NSObject> )時,協議繼承了后者的方法,而不是前者。 最終協議是由一些繼承自NSObject實現的類實現的,所以所有繼承自NSObject實現的實例仍然有效。 但我跑題了。

其實還是有辦法比較隊列的。

設置隊列時,請確保添加標簽。 出於我的目的,我有一個共享隊列,用於訪問數據庫以防止數據庫鎖定。 在我的 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);

獲取當前隊列的標簽並與定義的標簽進行比較。

如果您只對當前的 QOS 感興趣,請檢查Thread.current.qualityOfService的值。

我有原始帖子提到的相同功能要求。 您應該能夠在任何隊列上調用此異步函數,但如果在主隊列上調用,則回調到主隊列上的用戶。 我只是像這樣處理它:

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

獲取當前隊列的標簽並使用定義的標簽進行比較。

let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM