`
jishublog
  • 浏览: 865991 次
文章分类
社区版块
存档分类
最新评论

Linux线程同步之读写锁(rwlock)

 
阅读更多

读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程。当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步,

和互斥量不同的是:互斥量会把试图进入已保护的临界区的线程都阻塞;然而读写锁会视当前进入临界区的线程和请求进入临界区的线程的属性来判断是否允许线程进入。

相对互斥量只有加锁和不加锁两种状态,读写锁有三种状态:读模式下的加锁,写模式下的加锁,不加锁

读写锁的使用规则:

  • 只要没有写模式下的加锁,任意线程都可以进行读模式下的加锁;
  • 只有读写锁处于不加锁状态时,才能进行写模式下的加锁;

读写锁也称为共享-独占(shared-exclusive)锁,当读写锁以读模式加锁时,它是以共享模式锁住,当以写模式加锁时,它是以独占模式锁住。读写锁非常适合读数据的频率远大于写数据的频率从的应用中。这样可以在任何时刻运行多个读线程并发的执行,给程序带来了更高的并发度。

需要提到的是:读写锁到目前为止仍然不是属于POSIX标准,本文讨论的读写锁函数都是有OpenGroup定义的的。例如下面是在我机器上,编译器是gccversion4.4.6,关于读写锁的定义是包含在预处理命令中的:

#if defined __USE_UNIX98 || defined __USE_XOPEN2K

... 读写锁相关函数声明...

#endif

1读写锁的初始化和销毁

/* Initialize read-write lock  */
 int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock,
                                __const pthread_rwlockattr_t *__restrict __attr);

/* Destroy read-write lock */
extern int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock);

                                               返回值:成功返回0,否则返回错误代码

上面两个函数分别由于读写锁的初始化和销毁。和互斥量,条件变量一样,如果读写锁是静态分配的,可以通过常量进行初始化,如下:

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

也可以通过pthread_rwlock_init()进行初始化。对于动态分配的读写锁由于不能直接赋值进行初始化,只能通过这种方式进行初始化。pthread_rwlock_init()第二个参数是读写锁的属性,如果采用默认属性,可以传入空指针NULL

那么当不在需要使用时及释放(自动或者手动)读写锁占用的内存之前,需要调用pthread_rwlock_destroy()进行销毁读写锁占用的资源。

2读写锁的属性设置

/* 初始化读写锁属性对象 */
int pthread_rwlockattr_init (pthread_rwlockattr_t *__attr);

/* 销毁读写锁属性对象 */
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *__attr);

/* 获取读写锁属性对象在进程间共享与否的标识*/
int pthread_rwlockattr_getpshared (__const pthread_rwlockattr_t * __restrict __attr,
                                          int *__restrict __pshared);

/* 设置读写锁属性对象,标识在进程间共享与否  */
int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *__attr, int __pshared);

                                                    返回值:成功返回0,否则返回错误代码
这个属性设置和互斥量的基本一样,具体可以参考互斥量的设互斥量的属性设置

3读写锁的使用

/* 读模式下加锁  */
int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock);

/* 非阻塞的读模式下加锁  */
int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock);

# ifdef __USE_XOPEN2K
/*  限时等待的读模式加锁 */
int pthread_rwlock_timedrdlock (pthread_rwlock_t *__restrict __rwlock,
                                       __const struct timespec *__restrict __abstime);
# endif

/* 写模式下加锁  */
int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock);

/* 非阻塞的写模式下加锁 */
int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock);

# ifdef __USE_XOPEN2K
/* 限时等待的写模式加锁 */
int pthread_rwlock_timedwrlock (pthread_rwlock_t *__restrict __rwlock,
                                       __const struct timespec *__restrict __abstime);
# endif

/* 解锁 */
int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock);

                                                   返回值:成功返回0,否则返回错误代码

1pthread_rwlock_rdlock()系列函数

pthread_rwlock_rdlock()用于以读模式即共享模式获取读写锁,如果读写锁已经被某个线程以读模式占用,那么调用线程就被阻塞。在实现读写锁的时候可以对共享模式下锁的数量进行限制(目前不知如何限制)。

pthread_rwlock_tryrdlock()pthread_rwlock_rdlock()的唯一区别就是,在无法获取读写锁的时候,调用线程不会阻塞,会立即返回,并返回错误代码EBUSY

pthread_rwlock_timedrdlock()是限时等待读模式加锁,时间参数structtimespec*__restrict__abstime也是绝对时间,和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()3条件变量的使用

2pthread_rwlock_wrlock()系列函数

pthread_rwlock_wrlock()用于写模式即独占模式获取读写锁,如果读写锁已经被其他线程占用,不论是以共享模式还是独占模式占用,调用线程都会进入阻塞状态。

pthread_rwlock_trywrlock()在无法获取读写锁的时候,调用线程不会进入睡眠,会立即返回,并返回错误代码EBUSY

pthread_rwlock_timedwrlock()是限时等待写模式加锁,也和条件变量的pthread_cond_timedwait()使用基本一致,具体可以参考pthread_cond_timedwait()3条件变量的使用

3pthread_rwlock_unlock()

无论以共享模式还是独占模式获得的读写锁,都可以通过调用pthread_rwlock_unlock()函数进行释放该读写锁。

下面是测试代码:

#include <iostream>
#include <cstdlib>

#include <unistd.h>
#include <pthread.h>

using namespace std;

struct{
    pthread_rwlock_t rwlock;
    int product;
}sharedData = {PTHREAD_RWLOCK_INITIALIZER, 0};

void * produce(void *ptr)
{
    for (int i = 0; i < 5; ++i)
    {
        pthread_rwlock_wrlock(&sharedData.rwlock);
        sharedData.product = i;
        pthread_rwlock_unlock(&sharedData.rwlock);

        sleep(1);
    }
}

void * consume1(void *ptr)
{
    for (int i = 0; i < 5;)
    {
        pthread_rwlock_rdlock(&sharedData.rwlock);
        cout<<"consume1:"<<sharedData.product<<endl;
        pthread_rwlock_unlock(&sharedData.rwlock);

       ++i;
        sleep(1);
    }
}

void * consume2(void *ptr)
{
    for (int i = 0; i < 5;)
    {
        pthread_rwlock_rdlock(&sharedData.rwlock);
        cout<<"consume2:"<<sharedData.product<<endl;
        pthread_rwlock_unlock(&sharedData.rwlock);

        ++i;
        sleep(1);
    }
}

int main()
{
    pthread_t tid1, tid2, tid3;

    pthread_create(&tid1, NULL, produce, NULL);
    pthread_create(&tid2, NULL, consume1, NULL);
    pthread_create(&tid3, NULL, consume2, NULL);

    void *retVal;

    pthread_join(tid1, &retVal);
    pthread_join(tid2, &retVal);
    pthread_join(tid3, &retVal);

    return 0;
}

测试结果如下:

consume1:0
consume2:0
consume2:0
consume1:1
consume2:1
consume1:2
consume2:2
consume1:3
consume2:3
consume1:4

如果把consume1的解锁注释掉,如下:

void * consume1(void *ptr)
{
    for (int i = 0; i < 5;)
    {
        pthread_rwlock_rdlock(&sharedData.rwlock);
        cout<<"consume1:"<<sharedData.product<<endl;
        //pthread_rwlock_unlock(&sharedData.rwlock);

        ++i;
        sleep(1);
    }
}

程序的执行结果如下:

consume1:0
consume2:0
consume2:0
consume1:0
consume2:0
consume1:0
consume2:0
consume1:0
consume2:0
consume1:0

从执行结果可以看出Linux2.6.18提供的读写锁函数是优先考虑等待读模式占用锁的线程,这种实现的一个很大缺陷就是出现写入线程饿死的情况。


Jun 26, 2013 AM 00:08 @Library

分享到:
评论

相关推荐

    读写锁rwlock读写锁rwlock

    读写锁rwlock读写锁rwlock

    Linux系统编程之线程同步

    3. 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高 读写锁也叫共享-独占锁。当...

    linux 读写锁应用实例

    /*使用读写锁实现四个线程读写一段程序的实例,共创建了四个新的线程,其中两个线程用来读取数据,另外两个线程用来写入数据。在任意时刻,如果有一个线程在写数据,将阻塞所有其他线程的任何操作。*/

    e语言-易语言线程安全之原子锁与读写锁

    .版本 2 .支持库 spec .........局部变量 rwLock, ReadWriteLock ...' 本名称子程序用作测试程序用,仅在开发及调试环境中有效,编译发布程序前将被系统自动清空,请将所有用作测试的...rwLock.ReleaseWriteLock ()

    写优先防止读线程饿死的读写锁

    标准库std::mutex, std::condition_variable实现的读写锁,写优先,通过优先级控制防止读线程饿死。在支持c++11不支持C++14的工程使用。

    windows 读写锁 (基于关键区的读写锁类 及自动锁)

    读写锁的类 及demo 基于网上的类 改写了几处逻辑不合理的地方 :1、新的读锁来了如已有写锁等待 新的读锁进入等待 2、等待的读锁获取到执行权限后 把正等待的所有读锁全部激活

    rwlock:基于Redis和Lua的分布式读写锁,使用逻辑接近go的sync.RWMutex

    洛克使用方式go get github.com/wangfeiso/rwlock特性说明为确保锁的公平性,用Lua实现了优先级级别FCFS,当多个客户端获取写锁(排它锁)的时候,先到的会先获得锁。快速使用import ("github.com/wangfeiso/rwlock...

    用Python实现读写锁的示例代码

    Python 提供的多线程模型中并没有提供读写锁,读写锁相对于单纯的互斥锁,适用性更高,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁。 通俗点说就是当没有写锁时,就可以加读锁且任意...

    Python读写锁实现实现代码解析

    Python 提供的多线程模型中并没有提供读写锁,读写锁相对于单纯的互斥锁,适用性更高,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁。 通俗点说就是当没有写锁时,就可以加读锁且任意...

    读写锁 改进版

    资源中有demo和源码,基于RWLock类的改进版,原本读写锁在高频率读数据的时候会导致写线程抢占不到而死锁;本人对此做了改进,修正了锁阀门,经过测试可以放心使用了。 开发环境vs2003以上均可。

    posix pthread tools: barrie and rwlock

    posix pthread实现的多线程同步工具源代码,包括两个: 1. barrier 等待所有线程进入同一状态。 2. rwlock 读写锁。 多线程编程&gt;&gt;

    RWLock:这是一个windows下的读写锁的实现,有win7原生支持的版本,也有xp的版本

    RWLock 这是一个windows下的读写锁的实现,有win7原生支持的版本,也有xp的版本

    Linux系统内核抢占补丁的原理

    Linux 2.4内核正好细化了多CPU下的内核线程同步机构,对不可并行的指令块用spinlock和rwlock作了细致的表示,该补丁的实现可谓水到渠成。具 体的方法就是在进程的任务结构上增加一个preempt_count变量作为内核抢占锁...

    rwlock:Haxe 的读写锁

    读写锁 应用程序接口 创建 new( maxReaders, ?waitLogTimeout, ?waitLogger) 功能级别 read(func, ?wait) write(func, ?wait) 小鬼水平 // read prepareRead(?wait) releaseRead() // write prepareWrite(?wait) ...

    Linux内核的同步机制

     在主流的Linux内核中包含了几乎所有现代的操作系统具有的同步机制,这些同步机制包括:原子操作、信号量(semaphore)、读写信号量(rw_semaphore)、spinlock、BKL(Big Kernel Lock)、rwlock、brlock(只包含在...

    Concurrency Freaks:具有可扩展(且快速)读写锁的库-开源

    该库包含用于多个读写锁的Java代码:-可扩展读写锁:可读写锁,随执行读锁的线程数量而扩展。 -可扩展RWLockS:与上述类似,但实现方式不同。 -可扩展缩放的RWLock:可扩展的RWLock和JDK 8中提供的StampedLock的混合...

    《深入理解 Rust 并发编程》

    同步原语:各种同步原语进行了深入分析,如互斥锁(Mutex)、读写锁(RwLock)、条件变量(Condvar)、屏障(Barrier)等。 并发集合:如何安全地在多线程环境中使用集合类型,如 Vec、HashMap

    opt-rwlock:有效的RWLock支持在不同线程上的锁定和解锁

    opt-rwlock 有效的RWLock支持在不同线程上的锁定和解锁。

    前端开源库-rwlock

    前端开源库-rwlockrwlock,节点的读/写锁实现。

    Linux操作系统内核抢占补丁的基本原理

    2.4内核正好细化了多CPU下的内核线程同步机构,对不可并行的指令块用 spinlock和rwlock作了细致的表示,该补丁的实现可谓水到渠成。具体的方法就是在进程的任务结构上增加一个preempt_count变量作 为内核抢占锁,它...

Global site tag (gtag.js) - Google Analytics