【问题标题】:Recursive pthread_rwlock_rdlock on Mac OS X/DarwinMac OS X/Darwin 上的递归 pthread_rwlock_rdlock
【发布时间】:2012-01-10 14:01:28
【问题描述】:

我有以下通过线程执行的示例代码(参见下面的代码):

A: rd-lock
B: wr-lock (waiting)
A: rd-lock (recursive)
A: rd-unlock (recursive)
A: rd-unlock
B: wr-locked (wake after wait)
B: wr-unlock.

基本上读锁是递归的。它是 POSIX 标准所要求的(要求读锁是递归的,但没有为写锁指定)。这有效 在 Linux、FreeBSD、Solaris 上,但它不适用于 Darwin/Mac OS X。

下面的示例在 Linux 上给出以下输出:

read locking
read locked
write locking
read locking 2
read locked 2
read unlocked 2
read unlocked
write locked
write unlocked 2

在达尔文上打印:

read locking
read locked
write locking
read locking 2

这里死锁(不继续),基本上不尊重递归读锁。

有什么可以做的(标记、定义、链接到一个特殊的库版本)可以按预期工作吗?


示例代码

#include <pthread.h>
#include <stdio.h>

pthread_rwlock_t lock;

void *thread_r(void *p)
{
    printf("read locking\n");
    pthread_rwlock_rdlock(&lock);
    printf("read locked\n");
    usleep(500*1000);
    printf("read locking 2\n");
    pthread_rwlock_rdlock(&lock);
    printf("read locked 2\n");
    usleep(500*1000);
    pthread_rwlock_unlock(&lock);
    printf("read unlocked 2\n");
    usleep(500*1000);
    pthread_rwlock_unlock(&lock);
    printf("read unlocked\n");
}

void *thread_w(void *p)
{
    usleep(250*1000);
    printf("write locking\n");
    pthread_rwlock_wrlock(&lock);
    printf("write locked\n");
    pthread_rwlock_unlock(&lock);
    printf("write unlocked 2\n");
}

int main()
{
    pthread_t a,b;
    pthread_rwlock_init(&lock,NULL);
    pthread_create(&a,NULL,thread_r,0);
    pthread_create(&b,NULL,thread_w,0);
    pthread_join(a,NULL);
    pthread_join(b,NULL);
    return 0;
}

【问题讨论】:

    标签: macos pthreads rwlock


    【解决方案1】:

    只有 rdlock() 支持递归锁定:

    http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_rwlock_rdlock.html

    根据 Unix 规范,如果线程已经持有读锁或写锁,则调用 wrlock() 的行为是未定义的:

    http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_rwlock_trywrlock.html

    在使用 OS X 时,请查看 NSRecursiveLock:

    http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html

    【讨论】:

    • 只有当您当前有 wrlock 时尝试 rdlock 时,这才是未定义的。这种情况是单线程做两个rdlocks。
    【解决方案2】:

    是的,rwlocks 上的锁是读锁,在某种程度上确实是递归的。但是POSIX docs for pthread_rwlock_rdlock 中有一行,SUSv2 因为这是 Apple 支持的:

    如果写者没有持有锁并且没有写者阻塞在锁上,则调用线程获取读锁。当写者不持有锁并且有写者在等待锁时,调用线程是否获取锁是未指定的。

    完全没有关于让具有现有读锁的线程重新锁定以进行读取的内容。只是如果写入器被阻塞,读锁请求将被阻塞(实现通常优先考虑写入锁以避免写入器饥饿)。

    Apple's own online docs也支持这个:

    pthread_rwlock_rdlock() 函数在 rwlock 上获得一个读锁,前提是 rwlock 目前没有为写而持有,并且目前没有写线程被锁上阻塞。

    然后:

    为了防止作家挨饿,作家比读者更受青睐。

    再次。没有提到当写锁在队列中时允许递归读锁。

    【讨论】:

    • 引用"一个线程可能在rwlock上持有多个并发读锁(即成功调用pthread_rwlock_rdlock()函数n次)。如果是这样,该线程必须执行匹配解锁(即它必须调用 pthread_rwlock_unlock() 函数 n 次)。”此外,您给出的第一个引用是指“获取读锁”而不是线程已经获取此类锁的情况。正确的是,如果存在挂起的写锁,则无法获取新的“读锁”,但可以重新获取现有的读锁”
    • @Artyom,这是一个解释问题,我认为这就是实现不同的地方。 获取读锁作为一个动作可能包括递归获取(你似乎假设第二个 rdlock 不是获取,我是说它可能被认为是这样),所以它会被阻止等待等待写锁请求。换句话说,虽然它声明来自单个线程的多个 rdlock 是可以的,但它似乎并没有完全指定当 wrlock 挂起时执行第二个 rdlock 时会发生什么。
    • 我理解这一点,但如果你说允许递归锁(它明确表示),那么说你不能在这种情况下写锁,因为它可能会死锁,这使得所有递归锁在定义上都是无用的。因此,即使您以这种方式“解释”手册,支持递归锁定仍然没有意义。例如对于 Windows Vista/7 Slim RWLock,它明确表示禁止递归锁:msdn.microsoft.com/en-us/library/windows/desktop/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多