【问题标题】:How to implement a reentrant locking mechanism in objective-c through GCD?如何通过GCD在objective-c中实现可重入锁机制?
【发布时间】:2013-10-29 22:11:00
【问题描述】:

我有一个包含一些方法的 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

}];

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

【问题讨论】:

  • 您是否考虑改用@synchronized
  • @MartinR,是的,但是@synchronized 是一个经典的锁,不是基于 GCD/队列,因此,据我了解,出于代码简单性和性能的原因,不鼓励使用它.因此,这个问题的标题具有误导性,因为它包含“锁定”。我的意思是以可重入方式与 GDC/队列同步访问。我没有比说“可重入锁定”更好的词了,因为这是大多数人都知道的问题的解决方案的名称。
  • @MartinR,此外,尽管@synchronized 具有为可重入-whatever- 带来更简单代码的优势(如何最好地调用它?)。不过,它背后的锁定机制的性能不如 GCD 队列背后的机制。

标签: objective-c locking grand-central-dispatch reentrantlock


【解决方案1】:

第一件事:dispatch_get_current_queue() 已弃用。现在规范的方法是使用dispatch_queue_set_specific。一个这样的示例可能如下所示:

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 的调用并维护自己的带外目标图等来解决这个问题,但这留给读者作为练习。)

你接着说:

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

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

【讨论】:

  • 感谢您对已弃用的功能以及改用什么以及如何使用的解释。仅此一项就很有价值。
  • @ipmcc,你对这个主题和链接主题的回答和 cmets 给我留下了深刻的印象。您能否也看看我刚刚发布的这个question
  • @ipmcc 与使用带有标签和dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) 的队列相比,这种方法的有效性或效率是否存在差异?
  • “标签”具有固有的语义含义(描述或标识队列的人类可读字符串)。 “特定”数据槽的存在专门表示 。如果您要选择一个用于创建“重入锁定系统”(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-16
  • 1970-01-01
  • 2010-11-15
  • 2013-02-13
相关资源
最近更新 更多