嵌入式Linux:线程同步(读写锁)

目录

1、读写锁的初始化

2、销毁读写锁

3、读写锁加锁与解锁

4、读写锁的属性


在Linux中,读写锁(Read-Write Lock)提供了一种同步机制,允许多个线程并发读取共享资源,但只有一个线程可以对该资源进行写操作。读写锁相比互斥锁(mutex)或自旋锁(spinlock)具有更高的并行性,因为它有三种状态:读加锁状态、写加锁状态和不加锁状态。

读写锁的规则和状态:

  • 写模式加锁状态:当一个线程获取写锁时,其他所有试图获取该锁的线程(无论是读锁还是写锁)都会被阻塞,直到写锁被释放。

  • 读模式加锁状态:当线程获取读锁时,其他试图获取读锁的线程可以并发成功获取锁,但任何试图获取写锁的线程会被阻塞,直到所有读锁被释放。

读写锁的使用场景:

  • 适用于读操作频繁且写操作较少的情况,这样能够允许多线程并发读取,减少锁的竞争,提高系统的效率。
  • 当需要保护一个共享数据结构,同时支持多个线程读,但限制只有一个线程写时,读写锁是比简单的互斥锁更好的选择。

1、读写锁的初始化

在使用读写锁之前,必须对其进行初始化。Linux使用pthread_rwlock_t数据类型来表示读写锁,初始化方式有以下两种:

静态初始化

cpp 复制代码
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

动态初始化 : 使用pthread_rwlock_init()函数初始化:

cpp 复制代码
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);

参数说明

  • rwlock:指向需要初始化的读写锁对象(类型为pthread_rwlock_t)。
  • attr:指向读写锁属性的指针(类型为pthread_rwlockattr_t),可以设置为NULL,表示使用默认属性。

返回值

  • 成功返回0
  • 失败返回非0错误码,如:
    • EINVAL:表示无效的属性值或锁对象。
    • EBUSY:锁已初始化。
    • ENOMEM:系统内存不足。

2、销毁读写锁

当不再需要使用读写锁时,应该使用pthread_rwlock_destroy()销毁它。函数原型如下:

cpp 复制代码
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要销毁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回非0错误码,如:EBUSY:锁被其他线程持有。

3、读写锁加锁与解锁

以读模式加锁,该函数会阻塞调用线程,直到能够成功获取读锁。如果已经有其他线程持有写锁,当前线程将会等待。

cpp 复制代码
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要加锁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回非0错误码,如:
    • EINVAL:无效的锁。
    • EDEADLK:检测到死锁。
    • EAGAIN:系统无法分配更多的读锁。

以写模式加锁,该函数会阻塞调用线程,直到能够成功获取写锁。只有当前线程能够获取写锁,其他所有请求锁的线程(无论是读锁还是写锁)都会被阻塞。

cpp 复制代码
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要加锁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回非0错误码,如:
    • EINVAL:无效的锁。
    • EDEADLK:检测到死锁。

尝试获取读锁,该函数尝试获取读锁,不会阻塞。如果锁被其他线程占用,立即返回失败。

cpp 复制代码
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要加锁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回EBUSY表示锁已被占用,当前无法获取。

尝试获取写锁,该函数尝试获取写锁,不会阻塞。如果锁被其他线程占用,立即返回失败。

cpp 复制代码
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要加锁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回EBUSY表示锁已被占用,当前无法获取。

该函数用于释放当前线程持有的锁,无论是读锁还是写锁。

cpp 复制代码
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

参数说明

  • rwlock:指向需要解锁的读写锁对象。

返回值

  • 成功返回0
  • 失败返回非0错误码,如:
    • EINVAL:无效的锁对象。
    • EPERM:当前线程未持有该锁。

4、读写锁的属性

读写锁也可以有属性,使用pthread_rwlockattr_t数据类型来表示。

初始化读写锁属性函数原型:

cpp 复制代码
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);

参数说明

  • attr:指向需要初始化的读写锁属性对象。

返回值

  • 成功返回0
  • 失败返回非0错误码。

读写锁的属性中最常见的是进程共享属性,使用以下函数设置或获取:

cpp 复制代码
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);

参数说明

  • attr:指向需要设置的读写锁属性对象。
  • pshared:共享属性的值。取值如下:
    • PTHREAD_PROCESS_SHARED:允许多个进程共享该读写锁。
    • PTHREAD_PROCESS_PRIVATE:仅限当前进程的线程共享读写锁(默认值)。

返回值

  • 成功返回0
  • 失败返回非0错误码。

以下代码展示了如何在读写锁的保护下,允许多个线程并发读取共享资源,但只有一个线程可以修改它:

cpp 复制代码
#include <pthread.h>
#include <stdio.h>

pthread_rwlock_t rwlock;
int shared_data = 0;

void *reader(void *arg) {
    pthread_rwlock_rdlock(&rwlock);  // 获取读锁
    printf("Reader: Shared data is %d\n", shared_data);
    pthread_rwlock_unlock(&rwlock);  // 解锁
    return NULL;
}

void *writer(void *arg) {
    pthread_rwlock_wrlock(&rwlock);  // 获取写锁
    shared_data += 1;
    printf("Writer: Updated shared data to %d\n", shared_data);
    pthread_rwlock_unlock(&rwlock);  // 解锁
    return NULL;
}

int main() {
    pthread_t r1, r2, w1;

    pthread_rwlock_init(&rwlock, NULL);  // 初始化读写锁

    pthread_create(&r1, NULL, reader, NULL);
    pthread_create(&w1, NULL, writer, NULL);
    pthread_create(&r2, NULL, reader, NULL);

    pthread_join(r1, NULL);
    pthread_join(w1, NULL);
    pthread_join(r2, NULL);

    pthread_rwlock_destroy(&rwlock);  // 销毁读写锁
    return 0;
}

Linux中的读写锁适用于提高读密集型应用的并发性。它能够让多个线程同时读取资源,从而减少锁争用,但也需要合理考虑写饥饿问题。注意事项如下:

  • 读写锁的潜在问题:如果读操作过于频繁,可能导致写线程长时间无法获取写锁,从而引发写饥饿问题。这通常需要通过其他机制(如优先级)来控制。
  • 使用场景:当读操作远多于写操作时,读写锁能带来性能提升。如果写操作频繁,读写锁可能并不会比互斥锁表现更好。
相关推荐
minji...3 分钟前
linux 进程控制(一) (fork进程创建,exit进程终止)
linux·运维·服务器·c++·git·算法
I · T · LUCKYBOOM6 分钟前
21.Linux网络设置
linux·运维·网络
Likeyou77 分钟前
关于Linux下的Oracle的rman备份操作指南
linux·运维·oracle
峰顶听歌的鲸鱼11 分钟前
13.docker部署
linux·运维·笔记·docker·容器·云计算
橘子编程13 分钟前
仓颉语言变量与表达式解析
java·linux·服务器·开发语言·数据库·python·mysql
挽天java16 分钟前
智能终端开发文档
嵌入式
虚神界熊孩儿19 分钟前
linux下创建用户和用户组
linux·运维·服务器
hhwyqwqhhwy22 分钟前
linux 驱动 rtc
linux·运维·实时音视频
python百炼成钢23 分钟前
53.Linux regmap驱动框架
linux·运维·服务器·驱动开发
python百炼成钢26 分钟前
54.Linux IIO驱动框架
linux·运维·服务器·驱动开发