本文介绍了线程的同步对象读写锁的属性,通过读写锁属性可以控制在线程之间使用读写锁同步时的行为。

线程的读写锁仅支持进程共享属性。

属性介绍

数据类型:pthread_rwlockattr_t

初始化相关函数:

#include <pthread.h>

int pthread_rwlockattr_destroy(pthread_rwlockattr_t* attr);
int pthread_rwlockattr_init(pthread_rwlockattr_t* attr);
// 返回值:成功返回0,失败返回错误编号

pthread_rwlockattr_init()函数使用默认值初始化读写锁属性attr

进程共享属性

与互斥量的进程共享属性类似,用于进程间同步访问共享变量。

下面两个函数用于获取以及设置读写锁的进程共享属性:

#include <pthread.h>

int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t* restrict attr, int* restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t* attr, int pshared);
// 返回值:成功返回0,失败返回错误编号

可取值为PTHREAD_PROCESS_PRIVATEPTHREAD_PROCESS_SHARED,默认值为PTHREAD_PROCESS_PRIVATE

示例

下面的示例中,演示了读写锁是如何进行多进程间同步的。

父子进程在共享内存中的读写锁的保护下对计数器进行加法操作。

但是这个例子中并没有给出,父子进程中其中一个突然意外终止,其他进程如何来恢复的处理。因为有一个进程突然终止,其他进程就会一直阻塞。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define SHARED_MEMORY_KEY 1234
#define SHARED_MEMORY_SIZE 1024

typedef struct
{
    pthread_rwlock_t rwlock;  // 互斥量保证父子进程之间的同步
    int counter;              // 父子进程之间对该变量进行同步+1
} SharedData;                 // 共享内存数据

void* childProcess(void* arg)
{
    SharedData* sharedData = (SharedData*)arg;

    // 加锁
    pthread_rwlock_wrlock(&(sharedData->rwlock));
    printf("Child process: Counter before increment: %d\n", sharedData->counter);
    // 增加计数器
    sharedData->counter++;
    printf("Child process: Counter after increment: %d\n", sharedData->counter);
    // 解锁
    pthread_rwlock_unlock(&(sharedData->rwlock));

    return NULL;
}

int main()
{
    int sharedMemoryId;
    SharedData* sharedData;

    // 创建共享内存
    sharedMemoryId = shmget(SHARED_MEMORY_KEY, sizeof(SharedData), IPC_CREAT | 0666);
    if (sharedMemoryId < 0)
    {
        perror("shmget");
        exit(1);
    }

    // 连接到共享内存
    sharedData = shmat(sharedMemoryId, NULL, 0);
    if (sharedData == (void*)-1)
    {
        perror("shmat");
        exit(1);
    }

    // 初始化互斥量
    pthread_rwlockattr_t rwlockAttr;
    pthread_rwlockattr_init(&rwlockAttr);

    // 设置读写锁的进程共享属性,使其能够在父子进程之间同步
    pthread_rwlockattr_setpshared(&rwlockAttr, PTHREAD_PROCESS_SHARED);
    pthread_rwlock_init(&(sharedData->rwlock), &rwlockAttr);

    // 初始化计数器
    sharedData->counter = 0;

    // 创建子进程
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(1);
    }

    if (pid == 0)
    {
        // 子进程
        int i = 0;
        while (i < 5)
        {
            childProcess(sharedData);
            i++;
            sleep(1);
        }

        // 分离共享内存
        shmdt(sharedData);
    }
    else
    {
        int i = 0;
        while (i < 5)
        {
            // 加锁
            pthread_rwlock_wrlock(&(sharedData->rwlock));
            printf("Parent process: Counter before increment: %d\n", sharedData->counter);
            sharedData->counter++;
            printf("Parent process: Counter after increment: %d\n", sharedData->counter);
            // 解锁
            pthread_rwlock_unlock(&(sharedData->rwlock));

            i++;
            sleep(1);
        }

        // 等待子进程结束
        wait(NULL);
        // 分离共享内存
        shmdt(sharedData);
        // 删除共享内存
        shmctl(sharedMemoryId, IPC_RMID, NULL);
    }

    return 0;
}
// Parent process: Counter before increment: 0
// Parent process: Counter after increment: 1
// Child process: Counter before increment: 1
// Child process: Counter after increment: 2
// Child process: Counter before increment: 2
// Child process: Counter after increment: 3
// Parent process: Counter before increment: 3
// Parent process: Counter after increment: 4
// Child process: Counter before increment: 4
// Child process: Counter after increment: 5
// Parent process: Counter before increment: 5
// Parent process: Counter after increment: 6
// Parent process: Counter before increment: 6
// Parent process: Counter after increment: 7
// Child process: Counter before increment: 7
// Child process: Counter after increment: 8
// Parent process: Counter before increment: 8
// Parent process: Counter after increment: 9
// Child process: Counter before increment: 9
// Child process: Counter after increment: 10

存在问题

由于读写锁没有健壮属性,因此如果有其他进程在持有读写锁时终止,会导致其他进程阻塞在该读写锁上,恢复读写锁的工作需要使用者自己来处理。

因此正常情况下不推荐使用读写锁来进行进程间的同步工作