读写锁在pthread中的应用

5
我正在学习pthread,遇到了读写锁。场景很简单:一个全局变量被所有线程共享,读者不断打印该全局变量的当前值,而写者将更新同一变量。我可以通过使用两个互斥量(pthread_mutex_t)来实现此同步,但我想使用“一个”读写锁来实现相同的结果。然而,使用一个读写锁,如下面的输出所示,读者只能看到x的第一个值,并且看不到对该全局变量的任何更新。请在这里提供一些帮助。
代码:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include <poll.h>

#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

int x = 0;

pthread_rwlock_t lock_rw = PTHREAD_RWLOCK_INITIALIZER;

void *reader_thread(void *arg)
{
    int i;
    int newx, oldx;
    newx = oldx = -1;

    pthread_rwlock_t *p = (pthread_rwlock_t *)arg;

    if (pthread_rwlock_rdlock(p) != 0) {
        perror("reader_thread: pthread_rwlock_rdlock error");
        exit(__LINE__);
    }

    for (i = 0; i < 100; i++) {
        newx = ACCESS_ONCE(x);
        if (newx != oldx) {
            printf("reader_lock: x: %d\n",x);
        }
        oldx = newx;
        poll(NULL, 0, 1);
    }

    if (pthread_rwlock_unlock(p) != 0) {
        perror("reader thread: pthred_rwlock_unlock error");
        exit(__LINE__);
    }

    return NULL;
}

void *writer_thread(void *arg)
{
    int i;
    pthread_rwlock_t *p = (pthread_rwlock_t *)arg;

    if (pthread_rwlock_wrlock(p) != 0) {
        perror("writer thread: pthread_rwlock_wrlock error");
        exit(__LINE__);
    }

    for (i = 0; i < 3; i++) {
        ACCESS_ONCE(x)++;
        poll(NULL, 0, 5);
    }

    if (pthread_rwlock_unlock(p) != 0) {
        perror("writer thread: pthread_rwlock_unlock error");
        exit(__LINE__);
    }

    return NULL;
}

int main(void)
{
    pthread_t tid1, tid2;
    void *vp;
    if (pthread_create(&tid1, NULL, reader_thread, &lock_rw) != 0) {
        perror("pthread_create error");
        exit (__LINE__);
    }

    if (pthread_create(&tid2, NULL, writer_thread, &lock_rw) != 0) {
        perror("pthread_create error");
        exit (__LINE__);
    }

    //wait for the thread to complete
    if (pthread_join(tid1, &vp) != 0) {
        perror("pthread_join error");
        exit (__LINE__);
    }

    if (pthread_join(tid2, &vp) != 0) {
        perror("pthread_join error");
        exit (__LINE__);
    }

    printf("Parent process sees x: %d\n",x);
    return 0;
}

gcc pthread_rwlock.c -o rwlock -pthread -Wall -Werror

./rwlock

读取锁:x:0

父进程看到x:3


关于我的系统的一些信息,我的系统是64位的,运行在64位Linux v3.11.2上。 - Amit
1
为什么需要两个互斥锁来实现同步?我有什么遗漏的吗? - goldenmean
1个回答

9
当一个线程获得锁时,试图获取相同锁的其他线程将被挂起,直到第一个线程释放该锁。
这里发生的是,你的读取线程启动,获得锁并进入for/poll循环。
写入线程启动,尝试获取已被读取线程占用的锁,并在pthread_rwlock_wrlock上保持阻塞。
实际上,你想要做的是,在访问共享变量的代码前后放置你的lock/unlock
thread_rwlock_rdlock(p);
newx = ACCESS_ONCE(x);
thread_rwlock_unlock(p);
...
thread_rwlock_wrlock(p);
ACCESS_ONCE(x)++;
thread_rwlock_unlock(p);

这不就相当于只使用互斥锁(pthread_mutex_t)吗?在我的设置中,我尝试使用互斥锁代替读写锁,并将那些调用从pthread_rwlock_{rd|wr}lock更改为pthread_mutex_lock(),并将它们放置在那些热点周围,我实现了我想要的效果(读者可以看到x的所有中间值),但是,这种方法对我来说似乎不正确。读写锁应该带来一些额外的功能,不是吗? - Amit
1
@kumar,rwlock在标准互斥锁上并没有引入任何额外的安全保证。然而,它可能会减少高读取器争用的问题(但不一定,因为它更复杂且锁定速度较慢)。 - zch
从pthread_rwlock_wrlock的man页面中可以看到:“如果没有其他线程(读者或写者)持有读写锁rwlock,则调用线程将获取写锁。否则,该线程将阻塞,直到它可以获取锁为止。”因此,我原以为写入者不会被阻塞,但事实并非如此。我想我需要更深入地了解读写锁的使用情况。谢谢@zch。目前,我会采纳Flavio建议的方法。 - Amit
1
正如@zch所说,读写锁只有在许多读线程访问相同值时才会产生差异。如果只有一个读取器,则读写锁的行为与标准互斥锁相同。 - Flavio
1
@kumar:读写锁的目的是允许多个读者同时持有读锁,但仍然给予写者独占访问权。使用普通的互斥锁,所有线程都将具有独占访问权限。 - caf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接