【问题标题】:How do I determine if a thread has a lock?如何确定线程是否有锁?
【发布时间】:2011-04-23 18:33:45
【问题描述】:

我正在编写一个希望线程安全的 Objective-C 类。为此,我使用 pthreads 和 pthread_rwlock(使用 @synchronized 太过分了,我想了解更多关于 pthreads 的信息)。锁在init 方法指定的对象中初始化,并在dealloc 中销毁。我有三种操作锁的方法; readLockwriteLockunlock。这三个方法只是调用相关的 pthread 函数,目前没有别的。

这里有两个对象方法,都需要 writeLock:

-(void)addValue:(const void *)buffer
{
    [self writeLock];

    NSUInteger lastIndex = self.lastIndex;
    [self setValue:buffer atIndex:(lastIndex == NSNotFound) ? 0 : lastIndex+1];

    [self unlock];
}


-(void)setValue:(const void *)buffer atIndex:(NSUInteger)index
{
    [self writeLock];
    //do work here
    [self unlock];
}

调用setAddValue: 将首先获得一个写锁,然后调用setValue:atIndex:,它也将尝试获得一个写锁。文档指出,发生这种情况时行为未定义。因此,如何在尝试获取锁之前检查线程是否有锁?

(我可以确保临界区不进行触发另一个锁定请求的调用,但这意味着代码重复,我希望保持我的代码 DRY)。

【问题讨论】:

    标签: objective-c c pthreads posix readwritelock


    【解决方案1】:

    不完全清楚您使用的是哪种锁。您表明您正在使用 pthreads 和读/写锁,所以我得出的结论是您正在使用 pthread_rwlock。

    如果是这样,那么您应该可以在锁上使用pthread_rwlock_trywrlock。从手册页,

    如果成功,则 pthread_rwlock_wrlock() 和 pthread_rwlock_trywrlock() 函数将返回零。否则会返回错误号 来指示错误。

    而且,其中一个错误是:

    [EDEADLK] 调用线程已经拥有读/写锁 (用于阅读或写作)。

    因此,我相信你应该可以调用pthread_rwlock_trywrlock(),并且你会成功,如果另一个线程有锁,它会返回EBUSY,或者如果当前线程有锁,你会得到EDEADLK .

    【讨论】:

    • 此信息略有正确。 EDEADLK 错误仅针对 pthread_rwlock_(rd|wr)lock 指定,而不是 trylock 函数,在这种情况下它将返回 EBUSY 并且不区分锁是由调用线程持有还是由另一个线程持有。此外,只有当它是写锁时,才能检测到同一线程持有的锁。由于可以有无限数量的读者,因此无法检查持有锁的读者的身份。但是在 OP 的情况下,看起来双写锁是需要避免的问题,所以它可能没问题。
    • 另外,EDEADLK 错误只是“可能”,而不是“应该”,即不需要实现来检测和报告它
    • 进一步考虑,我认为您的解决方案行不通。我已经发布了一个替代方案作为答案。
    • R.,我正在阅读 OS X 上的手册页,它指出以下内容:
    • (grrr...hit return and ran out of editing time collect references) R.,我正在阅读 OS X 上的手册页 (developer.apple.com/library/mac/#documentation/Darwin/Reference/…),它指出以下内容:如果出现以下情况,pthread_rwlock_wrlock() 和 pthread_rwlock_trywrlock() 函数可能会失败: [EDEADLK] 调用线程已经拥有读/写锁(用于读取或写入)。 OP 可以很好地对行为进行经验测试。
    【解决方案2】:

    首先,只包含一个操作的临界区是没有用的。关键是使不同的事物相对于彼此同步。 (您确实有效地使整数原子化,但这可能不是全部意图。)

    其次,您已经知道在后面的关键部分中有写锁,因此无需检查它是否存在。只是不要在写入时尝试读锁。

    解决方案可能是将readLockwriteLock 调用移到调用函数中,但不知道更多是不可能的。

    (这也可能通过减少总操作数来降低锁定的性能成本,因为您不会锁定然后立即解锁。可能您不需要直接在 pthreads 级别工作。)

    【讨论】:

    • 第一句话是危险的错误。 C 中没有单一的原子操作。如果您正在编写 x86 程序集并使用带有 lock 前缀的内存操作数指令,那么您将是正确的,但否则您需要锁定。
    • @R..:我是说他正在做的锁定的效果是使整数原子化。或者至少,他正在这样做。自从我写下我的答案(奇怪的是,因为他接受了另一个答案),这个问题已经改变了。
    【解决方案3】:

    可移植程序不能依赖实现告诉调用者它已经持有写锁。相反,你需要做这样的事情来用递归写锁包装 rwlocks:

    int wrlock_wrap(pthread_rwlock_t *l, int *cnt)
    {
        int r = *cnt ? 0 : pthread_rwlock_wrlocK(l);
        if (!r) ++*cnt;
        return r;
    }
    
    int wrunlock_wrap(pthread_rwlock_t *l, int *cnt)
    {
        --*cnt;
        return pthread_rwlock_unlock(l);
    }
    

    您可以将计数保留在 pthread_rwlock_t 旁边的任何存储位置,例如作为你的结构/类/其他的成员。

    【讨论】:

    • 您关于可移植性的注释是正确的,但是 OP 显然是在 OS X 上,并且选择了 Objective C 和 Cocoa 框架,已经放弃了可移植性的考虑。我最初考虑了类似于您建议的实现(尽管将其直接粘贴在 OP 显示的实现中),但在检查 OS X 手册页后放弃了它(请参阅上面的参考资料)。
    猜你喜欢
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2010-09-22
    相关资源
    最近更新 更多