如何通过GCD在objective-c中实现可重入锁定机制?

如何通过GCD在objective-c中实现可重入锁定机制?,objective-c,locking,grand-central-dispatch,reentrantlock,Objective C,Locking,Grand Central Dispatch,Reentrantlock,我有一个objective-c类和一些方法,它们使用GCD队列来确保对资源的并发访问以串行方式进行(实现这一点的标准方法) 其中一些方法需要调用同一类的其他方法。因此,锁定机制需要重新进入。有没有标准的方法可以做到这一点 首先,我使用了这些方法中的每一种 dispatch_sync(my_queue, ^{ // Critical section }); 同步访问。如您所知,当这些方法中的一个调用另一个这样的方法时,会发生死锁,因为dispatch_sync调用会停止当前执行,直到执

我有一个objective-c类和一些方法,它们使用GCD队列来确保对资源的并发访问以串行方式进行(实现这一点的标准方法)

其中一些方法需要调用同一类的其他方法。因此,锁定机制需要重新进入。有没有标准的方法可以做到这一点

首先,我使用了这些方法中的每一种

dispatch_sync(my_queue, ^{

   // Critical section

});
同步访问。如您所知,当这些方法中的一个调用另一个这样的方法时,会发生死锁,因为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

}];

我不喜欢这个解决方案,因为对于每个具有不同返回类型的块,我需要编写另一个方法。此外,这个问题在我看来很常见,我认为应该有一个更好的标准解决方案。

第一件事:不推荐使用
dispatch\u get\u current\u queue()。现在,规范的方法是使用
分派队列集特定的
。其中一个示例可能如下所示:

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\u set\u target\u queue
创建嵌套的递归队列,并且尝试将外部队列中的工作从内部队列中排队将导致死锁,即使您已经“在锁中”(在嘲讽中引用,因为它看起来只是一个锁,所以它实际上是一个不同的东西:一个队列——这就是问题所在,对吧?)(你可以通过将调用包装到
dispatch\u set\u target\u queue
并维护你自己的带外目标图等来绕过这个问题,但这留给读者作为练习。)

你接着说:

我不喜欢这个解决方案,因为每一个区块都有不同的 返回类型,我需要编写另一个方法


这种“状态保护串行队列”模式的总体思想是,您正在保护私有状态;为什么要将“自己的队列”带到这里?如果是关于多个对象共享状态保护,则为它们提供一种查找队列的固有方法(即,要么在初始时间将其推入,要么将其放在所有相关方都可以访问的地方)。不清楚如何“自行排队”在这里会很有用。

谢谢你对不推荐使用的函数以及如何使用的解释。这本身就很有价值。@ipmcc,你对这个主题和链接主题的回答和评论给我留下了深刻的印象。你能看看我刚刚发布的这个吗?@ipmcc在效率和效率上有什么不同吗这种方法与使用带有标签的队列相比&
dispatch\u queue\u get\u label(dispatch\u CURRENT\u queue\u label)
?“label”具有固有的语义含义(人类可读的描述或标识队列的字符串)。“specific”数据槽的存在是专门用来表示的。如果您要选择一个来创建队列“重入式锁定系统”(您是否考虑使用<代码> @同步< /代码>?@ MartinR,是的,但是<代码> @同步是一个经典的锁,不是基于GCD/Queices的,因此,因为我理解,因为代码简单和性能的原因,它被劝阻使用。这个问题的标题是误导的,因为它包含了“锁”。“。我的意思是以可重入方式与GDC/queues同步访问。我没有比说“可重入锁定”更好的词了,因为这是大多数人都知道的问题解决方案的名称。@MartinR,此外,尽管
@synchronized
具有使可重入代码更简单的优点-无论什么-(如何称之为最佳?)不过,它背后的锁定机制的性能不如GCD队列背后的机制。