8

私は、GCDキューを使用して、リソースへの同時アクセスが連続して行われるようにするいくつかのメソッドを備えたobjective-cクラスを持っています(これを行う標準的な方法)。

これらのメソッドの一部は、同じクラスの他のメソッドを呼び出す必要があります。したがって、ロック機構は再入可能である必要があります。これを行う標準的な方法はありますか?

最初は、これらのメソッドのそれぞれを使用してもらいました

dispatch_sync(my_queue, ^{

   // Critical section

});

アクセスを同期します。ご存知のように、これらのメソッドの 1 つが別のそのようなメソッドを呼び出すと、dispatch_sync 呼び出しが他のブロックが実行されるまで現在の実行を停止するため、デッドロックが発生します。これは、キューでの実行が停止されるため実行できません。これを解決するために、たとえば次の方法を使用しました。

- (void) executeOnQueueSync:(dispatch_queue_t)queue : (void (^)(void))theBlock {
    if (dispatch_get_current_queue() == queue) {
        theBlock();
    } else {
        dispatch_sync(queue, theBlock);
    }
}

そして、私の方法のそれぞれで、私は使用します

[self executeOnQueueSync:my_queue : ^{

   // Critical section

}];

戻り値の型が異なるすべてのブロックに対して、別のメソッドを記述する必要があるため、このソリューションは好きではありません。さらに、この問題は私にとって非常に一般的なようであり、これに対するより優れた標準的な解決策が存在するはずです。

4

1 に答える 1

13

まず最初に:dispatch_get_current_queue()非推奨です。標準的なアプローチは、 を使用することになりますdispatch_queue_set_specific。そのような例の 1 つが次のようになります。

typedef dispatch_queue_t dispatch_recursive_queue_t;
static const void * const RecursiveKey = (const void*)&RecursiveKey;

dispatch_recursive_queue_t dispatch_queue_create_recursive_serial(const char * name)
{
    dispatch_queue_t queue = dispatch_queue_create(name, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_set_specific(queue, RecursiveKey, (__bridge void *)(queue), NULL);
    return queue;
}

void dispatch_sync_recursive(dispatch_recursive_queue_t queue, dispatch_block_t block)
{
    if (dispatch_get_specific(RecursiveKey) == (__bridge void *)(queue))
        block();
    else
        dispatch_sync(queue, block);
}

このパターンは非常に使いやすいですが、間違いなく防弾ではありません。なぜなら、ネストされた再帰キューを で作成できdispatch_set_target_queue、内側のキューの内側から外側のキューで作業をキューに入れようとすると、既に「ロックの内側」にいる場合でもデッドロックになるからです (嘲笑の引用は、それがロックのようにしか見えないためです。実際には何か違うものです: キュー — したがって、質問ですよね?) 外側のものについて. (独自のアウトオブバンド ターゲティング グラフなどへの呼び出しをラップして維持することで、これを回避できますがdispatch_set_target_queue、それは読者の課題として残されています。)

あなたは続けてこう言います:

戻り値の型が異なるブロックごとに、別のメソッドを作成する必要があるため、このソリューションは好きではありません。

この「状態を保護するシリアル キュー」パターンの一般的な考え方は、プライベートな状態を保護しているということです。なぜこれに「独自のキューを持ち込む」のですか?状態保護を共有する複数のオブジェクトに関する場合は、キューを見つけるための固有の方法をそれらに与えます (つまり、初期化時にプッシュするか、すべての関係者が相互にアクセスできる場所に配置します)。ここで「独自のキューを使用する」ことがどのように役立つかは明確ではありません。

于 2013-10-21T13:12:31.087 に答える