web-dev-qa-db-ja.com

現在のディスパッチキューを取得しますか?

私は、任意のキューから呼び出されることをサポートする必要があるメソッドを持っています。バックグラウンドスレッド自体でコードを実行し、ブロック引数に値を返すときにdispatch_get_main_queueを使用します。メソッドに入ったときでなければ、強制的にメインキューに入れたくありません。現在のディスパッチキューへのポインタを取得する方法はありますか?

44
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

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 sources を熟読すると、最終的には、「実行中のキューは何ですか?」という質問に対する複数の回答がある可能性があることがわかります。 (キューは最終的にグローバルキューのいずれかを対象とするためなど)

将来のブロックが特定のキューで実行されることを保証したい場合、唯一の方法は、APIが完了ブロックとともにパラメーターとしてキューを受け入れるようにすることです。これにより、呼び出し元は完了がどこで実行されるかを決定できます。

呼び出し元がメインスレッド上にあるかどうかを単純に知るだけで十分な場合は、_+[NSThread isMainThread]_を使用して確認できます。一般的なケースでは、メインGCDキューで実行されるすべてのブロックはメインスレッドで実行されます。 (このルールの1つの例外は、アプリケーションがメインの実行ループの代わりにdispatch_main()を使用する場合、メインキューで実行していることを確実に検出するために_dispatch_get_specific_と友人を使用する必要があります-これは比較的まれな状況です。)より一般的には、メインスレッドで実行されるすべてのコードがGCDを介してメインキューで実行されるわけではないことに注意してください。 GCDはメインスレッドの実行ループに従属します。特定のケースでは、それで十分かもしれません。

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は、私たちが呼び出されたキューにロジックをバインドすることを特に思いとどまらせるので、デバッグ目的のみにこれを利用するほうが良いと信じています。

15
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;
}

共有dbトランザクションキューは、すべての実行をデータベースにディスパッチするためにファイル内でローカルに使用されます。ただし、これには、トランザクション全体をデータベースにディスパッチできるようにするパブリックアクセサーもあります。したがって、内部的には、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);

現在のキューのラベルを取得し、定義されたラベルと比較します。

2
johnrechd

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)
1
Oleg Barinov

このNSOBjectのメソッドの代替アプローチとして performSelector:withObject:afterDelay: は、現在のスレッドの実行ループで呼び出しをディスパッチします。ドキュメントによると:

このメソッドは、現在のスレッドの実行ループでaSelectorメッセージを実行するタイマーを設定します。

明らかに、私はこれをゼロの遅延で使用することを提案しています、これは再びドキュメントによると:

0の遅延を指定しても、セレクターがすぐに実行されるとは限りません。セレクターはまだスレッドの実行ループのキューに入れられ、できるだけ早く実行されます。

残念ながら、引数は1つだけであるため、メソッドに多少の時間がかかる場合は、回避策が必要になる場合があります。

私が指摘したもう1つのことは、この方法はプロトコルではなく実装だけで利用できるということです。これは、このメソッドがNSObjectインターフェースではなくNSObjectカテゴリーに存在するためです(下記のPSを参照)。これは、idにキャストすることで簡単に修正できます。

PS:プロトコルと実装の2つの異なるNSObjectsが存在します。 NSObject宣言に注意してください。

@interface NSObject <NSObject> { ... }

奇妙に思えるかもしれませんが、宣言されています(@interface)ともう1つは以前に宣言されたプロトコル(<および>)。 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

現在のキューのラベルを取得し、使用して定義済みのラベルと比較します。

let queueName = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)
0