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

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

分享到:
评论

相关推荐

    c语言开发图书管理系统项目源码+数据+可运行程序

    c语言开发图书管理系统项目源码+数据+可运行程序 主要功能有:1、以管理员或读者不同身份注册账户。2、登录、找回密码、修改密码。3、管理员:图书入库,清除库存,统计书籍数量,统计读者借书情况,在馆书籍排序,读者排序。4、读者:查看个人借阅信息,借书,还书,按书名、作者、出版社检索图书,查询全部在馆图书。

    基于transformer的多模态脑肿瘤分割.zip

    本项目旨在利用Transformer模型实现多模态脑肿瘤分割。多模态脑肿瘤分割对于脑肿瘤的诊断和治疗具有重要意义。 我们采用Transformer模型,通过分析脑部MRI、CT、PET等不同模态的医学影像数据,实现对脑肿瘤区域的自动分割。项目使用的数据集包括公开的脑肿瘤影像数据集,如BRATS、Medical Segmentation Decathlon等,并进行了预处理,包括图像增强、分割和特征提取等。 在运行环境方面,我们使用Python编程语言,基于TensorFlow、PyTorch等深度学习框架进行开发。为了提高计算效率,我们还使用了GPU加速计算。此外,我们还采用了Docker容器技术,确保实验结果的可重复性。 项目完成后,将实现对多模态脑肿瘤的快速、准确分割,为脑肿瘤的诊断和治疗提供有力支持。同时,项目成果也可应用于其他医学影像分析领域。

    马尔文高浓度池说明书 High Concentration Cell (ZEN1010)

    马尔文高浓度池说明书 High Concentration Cell (ZEN1010)

    如何提高APP商业变现能力.docx

    如何提高APP商业变现能力.docx

    node-v12.20.0-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    R语言绘制SCI科研生存ROC曲线源代码.zip

    把input里面的数据替换成自己的数据,打开R,点Run,可以直接出图!文件适合有R语言基础的同学。

    高电压技术(第三版)课后习题答案

    高电压技术(第三版)课后习题答案

    Linux线程同步:深入理解与实践.pdf

    linux之线程同步一。 本文深入探讨了Linux线程同步的几种常见机制,并提供了代码示例。希望这些信息能够帮助你更好地理解和应用线程同步技术。在编写多线程程序时,请务必谨慎,确保线程安全。

    1999-2022年企业持续绿色创新水平数据.dta

    1999-2022年企业持续绿色创新水平数据.dta

    数据结构实验代码三个方法求最大公约数.rar

    数据结构实验代码

    机械设计无阀滤池-30Tsw16可编辑非常好的设计图纸100%好用.zip

    机械设计无阀滤池-30Tsw16可编辑非常好的设计图纸100%好用.zip

    机械设计RJ45安防线连接器焊锡机sw18可编辑非常好的设计图纸100%好用.zip

    机械设计RJ45安防线连接器焊锡机sw18可编辑非常好的设计图纸100%好用.zip

    SwiftUI嵌入UIKit(SwiftUI顺传值到UIKit)

    SwiftUI嵌入UIKit(SwiftUI顺传值到UIKit)

    node-v12.18.4-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    Python代码实现基于朴素贝叶斯算法的垃圾邮件分类(源码+全部数据)

    当处理垃圾邮件分类问题时朴素贝叶斯算法是一种经典且常用的方法。朴素贝叶斯算法基于贝叶斯定理特征条件独立性假设,能够高效地处理文本分类问题。 以下是一个使用Python实现基于朴素贝斯算法的垃圾邮件分类的示例: 1. 数据准备: 首先,需要准备训练数据集和测试数据集。训练数据集包含已标记的垃圾邮件和非垃圾邮件的文样本,通过这些样本进行模型训练。测试数据集用于评估模型的性能。 2. 数据预处理: 将文本样本转换为特征向量是朴素贝叶斯算法的关键步骤。可以使用词袋模型或者TF-IDF等方法将文本样本表示为向量。 3. 特征选择: 根据问题的具体特点,可以选择保留所有特征或者进行特征选择。常见的特征选择方法有卡方检验、互信息等。 4. 模型训练: 使用训练数据集训练朴素贝叶斯分类模型。常见的朴素贝叶斯分类器有多项式朴素贝叶斯、伯努利朴素贝叶斯和高斯朴素贝叶斯等。 5. 模型评估: 使用测试数据集评估模型的性能,常见的评估指标包括准确率、召回率、精确率和F1值等。 下面是一个简单的Python实现示例: ```python import numpy as np from

    httpsyy70958.com29875videoplayvid45958.m3u8..m3u

    httpsyy70958.com29875videoplayvid45958.m3u8..m3u

    Java 三维装箱代码示例

    在Java中,实现一个三维装箱(也称为三维背包问题)的算法通常涉及到组合优化和动态规划。这个问题是一个典型的优化问题,其中目标是在三个维度的限制下最大化价值的总和。下面是一个简单的Java代码示例,它使用动态规划来解决三维装箱问题。 请注意,这个代码只是一个简单的示例,它假设所有物品的第三个维度的大小都是1,并且没有给出如何回溯选择物品的完整逻辑。在实际应用中,三维装箱问题可能更加复杂,需要考虑所有三个维度的限制,并且可能需要更复杂的算法来解决。 此外,这个问题的解决方案可能需要根据具体问题的要求进行调整,例如物品是否可以分割、是否允许超过一个的物品等。如果你有特定的问题描述或者需要进一步的帮助,请提供更多的细节。

    BCSP-X 2024年活动文件,包含活动范围和模拟题

    北京市中小学信息学能力测评活动(BCSP-X)活动范围和模拟题目,包含基础知识题目和测试点题目

    商业模式画布——如何找到独特价值定位.docx

    商业模式画布——如何找到独特价值定位.docx

    SITOP 电源电源电源电源 20 6EP1 536-2AA00 操作手册

    SITOP 电源电源电源电源 20 6EP1 536-2AA00 操作手册

Global site tag (gtag.js) - Google Analytics