本文介绍了如何通过GCD在Objective-C中实现可重入锁定机制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有一些方法的Objective-C类,这些类使用GCD队列来确保对资源的并发访问是串行进行的(执行此操作的标准方法).

I have an objective-c class with some methods, which use a GCD queue to ensure that concurrent accesses to a resource take place serially (standard way to do this).

其中一些方法需要调用同一类的其他方法.因此,锁定机制需要重新输入.有标准的方法吗?

Some of these methods need to call other methods of the same class. So the locking mechanism needs to be re-entrant. Is there a standard way to do this?

起初,我使用了每种方法

At first, I had each of these methods use

dispatch_sync(my_queue, ^{

   // Critical section

});

同步访问.如您所知,当这些方法之一调用另一个这样的方法时,会发生死锁,因为dispatch_sync调用会停止当前执行,直到执行了另一个块为止,该块也无法执行,因为队列的执行已停止.为了解决这个问题,我然后使用了此方法:

to synchronize accesses. As you know, when one of these methods calls another such method, a deadlock happens because the dispatch_sync call stops the current executing until that other block is executed, which can't be executed also, because execution on the queue is stopped. To solve this, I then used e.g. this method:

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

在我的每种方法中,我都使用

And in each of my methods, I use

[self executeOnQueueSync:my_queue : ^{

   // Critical section

}];

我不喜欢这种解决方案,因为对于每个具有不同返回类型的块,我需要编写另一个方法.而且,这个问题在我看来非常普遍,我认为应该为此找到一个更好的标准解决方案.

I do not like this solution, because for every block with a different return type, I need to write another method. Moreover, this problem looks very common to me and I think there should exist a nicer, standard solution for this.

推荐答案

首先要解决的问题:已弃用dispatch_get_current_queue().现在的规范方法是使用dispatch_queue_set_specific.这样的一个例子可能看起来像:

First things first: dispatch_get_current_queue() is deprecated. The canonical approach would now be to use dispatch_queue_set_specific. One such example might look like:

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的调用并维护自己的带外定位图等方法来解决此问题,但这只是读者的一项练习.)

This pattern is quite usable, but it's arguably not bulletproof, because you could create nested recursive queues with dispatch_set_target_queue, and trying to enqueue work on the outer queue from inside the inner one would deadlock, even though you are already "inside the lock" (in derision quotes because it only looks like a lock, it's actually something different: a queue — hence the question, right?) for the outer one. (You could get around that by wrapping calls to dispatch_set_target_queue and maintaining your own out-of-band targeting graph, etc., but that's left as an exercise for the reader.)

您继续说:

这种状态保护串行队列"模式的总体思路是,您正在保护私有状态;你为什么要为此带上自己的队列"?如果是关于多个共享状态保护的对象,则给它们一种固有的方式来查找队列(即,在初始化时将其推入,或将其放置在所有相关方可相互访问的位置).目前尚不清楚带上自己的队列"在这里是否有用.

The general idea of this "state-protecting serial queue" pattern is that you're protecting private state; why would you "bring your own queue" to this? If it's about multiple objects sharing the state protection, then give them an inherent way to find the queue (i.e., either push it in at init time, or put it somewhere that's mutually accessible to all interested parties). It's not clear how "bringing your own queue" would be useful here.

这篇关于如何通过GCD在Objective-C中实现可重入锁定机制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-16 08:32