互斥锁,读写锁

柔光的暖阳◎ 2022-12-12 15:05 385阅读 0赞

一、线程互斥方式。 —- 互斥锁
1、什么是互斥锁?特点怎么样?
互斥锁是专门用于处理线程之间互斥的一种方式,它有两种:上锁状态/解锁状态。
如果互斥锁处于上锁状态,那么再上锁就会阻塞,直到这把锁解开之后,才能上锁。
如果互斥锁处于解锁状态,那么再解锁依然可以的,不会阻塞。

2、 互斥锁函数接口?
1)定义互斥锁变量 -> 数据类型: pthread_mutex_t
pthread_mutex_t m;

2)初始化互斥锁。 -> pthread_mutex_init() -> man 3 pthread_mutex_init
动态初始化:
头文件:
#include

原型:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

参数:
mutex:互斥锁变量的地址。
mutexattr:普通属性,填NULL。

返回值:
成功:0
失败:非0

静态初始化:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
只要把该宏定义赋值给互斥锁变量,就等价于初始化了这把互斥锁。

========================
也就是说:
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

等价于
pthread_mutex_t m;
pthread_mutex_init(&m,NULL);

3)上锁。 -> pthread_mutex_lock() -> man 3 pthread_mutex_lock
头文件:
#include

原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);

参数:
mutex:互斥锁变量的地址。

返回值:
成功:0
失败:非0

4)解锁。 -> pthread_mutex_unlock() -> man 3 pthread_mutex_unlock
头文件:
#include

原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex);

参数:
mutex:互斥锁变量的地址。

返回值:
成功:0
失败:非0

5)销毁互斥锁。 -> pthread_mutex_destroy() -> man 3 pthread_mutex_destroy
头文件:
#include

原型:
int pthread_mutex_destroy(pthread_mutex_t *mutex);

参数:
mutex:互斥锁变量的地址。

返回值:
成功:0
失败:非0

一般地:
多个线程任务都一样的 -> 互斥锁
多个线程任务不一样的 -> 无名信号量

练习1: 使用互斥锁完成5个线程打印helloworld。 -> 多个线程任务都一样的
练习2: 使用互斥锁完成<练习.docx> -> 多个线程任务不一样的

二、线程互斥方式。 — 读写锁
1、互斥锁有什么缺陷?
互斥锁无论是读取共享资源,还是修改共享资源,都要上锁,而且在上锁期间,不能被别的线程上锁。

访问资源(一起读一本书) -> 同时上读锁 -> 读锁就是一把共享锁。
修改资源(一起做一份试卷) -> 不能同时上写锁 -> 写锁就是一把互斥锁。

这把既有读锁,又有写锁的锁,就称之为读写锁。

2、读写锁函数接口?
1)定义一个读写锁变量。 数据类型: pthread_rwlock_t
pthread_rwlock_t rwlock;

2)初始化读写锁? -> pthread_rwlock_init() -> man 3 pthread_rwlock_init
动态初始化:
头文件:
#include

原型:
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);

参数:
rwlock: 读写锁的地址。
attr: 普通属性,填NULL。

返回值:
成功:0
失败:非0

静态初始化:
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

3)读锁上锁。 -> pthread_rwlock_rdlock() -> man 3 pthread_rwlock_rdlock
头文件:
#include

原型:
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

参数:
rwlock: 读写锁的地址。

返回值:
成功:0
失败:非0

4)写锁上锁。 -> pthread_rwlock_wrlock() -> man 3 pthread_rwlock_wrlock
头文件:
#include
原型:
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

参数:
rwlock: 读写锁的地址。

返回值:
成功:0
失败:非0

5)解锁。 -> pthread_rwlock_unlock() -> man 3 pthread_rwlock_unlock
头文件:
#include

原型:
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

参数:
rwlock: 读写锁的地址。

返回值:
成功:0
失败:非0

6)销毁读写锁。 -> pthread_rwlock_destroy() -> man 3 pthread_rwlock_destroy
头文件:
#include
原型:
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数:
rwlock: 读写锁的地址。

返回值:
成功:0
失败:非0

练习3: 现有一个临界资源: “int a” -> 全局变量
现在有4个线程,有两个线程想打印a的值 -> 读
thread1: 3S thread2:5S -> 看时间 -> 如果读完需要8S,则读锁不能同时上

有两个线程想修改a的值,一个想改成30,一个想改成50 -> 写
thread3: 4S thread4: 6S
再开一条线程,用于倒数时间就行。

验证:1)读锁可以同时上,写锁不可以同时上。
2)读锁与写锁能不能同时上? -> 不可以,读锁要等到写锁解开了之后才能上锁。

#include “head.h”

int a = 100; //临界资源
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

void *func_time(void *arg)
{
int i;
for(i=0;i<1000;i++)
{
printf(“i = %d\n”,i);
sleep(1);
}
}

//线程1: 打印a的值。 -> 3S (读操作)
void *func1(void *arg)
{
//1. 访问资源前,先上读锁。
pthread_rwlock_rdlock(&rwlock);
printf(“thread 1 rdlock lock!\n”);

//2. 打印a的值。
printf(“a = %d\n”,a);

//3. 持续3S
sleep(3);

//4. 访问完资源了,需要解锁。
pthread_rwlock_unlock(&rwlock);
printf(“thread 1 rdlock unlock!\n”);

//5. 线程退出
pthread_exit(NULL);
}

//线程2: 打印a的值。 -> 5S (读操作)
void *func2(void *arg)
{
//1. 访问资源前,先上读锁。
pthread_rwlock_rdlock(&rwlock);
printf(“thread 2 rdlock lock!\n”);

//2. 打印a的值。
printf(“a = %d\n”,a);

//3. 持续5S
sleep(5);

//4. 访问完资源了,需要解锁。
pthread_rwlock_unlock(&rwlock);
printf(“thread 2 rdlock unlock!\n”);

pthread_exit(NULL);
}

//线程3:修改a的值为50。 -> 4S
void *func3(void *arg)
{
//1. 修改资源之前,需要上写锁。
pthread_rwlock_wrlock(&rwlock);
printf(“thread 3 wrlock lock!\n”);

//2. 修改临界资源。
a = 50;

//3. 持续一段时间。
sleep(4);

//4. 修改资源后,需要解锁。
pthread_rwlock_unlock(&rwlock);
printf(“thread 3 wrlock unlock!\n”);

pthread_exit(NULL);

}

//线程4:修改a的值为30。 -> 6S
void *func4(void *arg)
{
//1. 修改资源之前,需要上写锁。
pthread_rwlock_wrlock(&rwlock);
printf(“thread 4 wrlock lock!\n”);

//2. 修改临界资源。
a = 100;

//3. 持续一段时间。
sleep(6);

//4. 修改资源后,需要解锁。
pthread_rwlock_unlock(&rwlock);
printf(“thread 4 wrlock unlock!\n”);

pthread_exit(NULL);
}

int main(int argc,char *argv[])
{
//0. 创建一个用于倒数时间线程
pthread_t tid_time;
pthread_create(&tid_time,NULL,func_time,NULL);

//1、 创建2个子线程,用于打印临界资源的值。
pthread_t tid1,tid2,tid3,tid4;
pthread_create(&tid1,NULL,func1,NULL);
pthread_create(&tid2,NULL,func2,NULL);
pthread_create(&tid3,NULL,func3,NULL);
pthread_create(&tid4,NULL,func4,NULL);

//2. 接合线程
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_join(tid4,NULL);

//3. 销毁读写锁
pthread_rwlock_destroy(&rwlock);

return 0;
}

发表评论

表情:
评论列表 (有 0 条评论,385人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    共享锁(S锁)又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S 锁。这保证了其他事务可以读A,但

    相关 互斥

    一、线程互斥方式。 --- 互斥锁 1、什么是互斥锁?特点怎么样? 互斥锁是专门用于处理线程之间互斥的一种方式,它有两种:上锁状态/解锁状态。 如果互斥锁处于上锁状