线程同步互斥机制

线程同步互斥机制

线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

例如 :
这里写图片描述
这里写图片描述
出现这种情况是因为操作系统在内核中对线程的调用的来回切换。代码可以并发的切换到其他的线程。

互斥锁

互斥锁以排他方式防止共享数据被并发访问。
互斥锁是一个二元变量,只有两种状态 开锁 和 上锁。
将某个共享资源与某个特定互斥锁在逻辑上绑定。

操作函数

在访问共享资源时,首先申请互斥锁,如果该互斥处于开锁状态,则申请到该锁对象,并立即上锁,防止其他线程访问该资源。如果互斥锁处于锁定状态,默认阻塞当前进程。
只有锁定该互斥锁的进程才能释放该互斥锁。

初始化锁

动态分配
在使用互斥锁前,需要定义该互斥锁

pthread_mutex_t lock;

pthread_mutex_init() 初始化函数

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);
第一个参数为要初始化的互斥锁。
第二个参数指向属性对象的指针,该属性对象定义要初始化的互斥锁的属性。

静态分配

利用宏 PTHREAD_MUTEX_INITIALIZER 初始化静态分配的互斥锁
销毁互斥锁

pthread_mutex_destroy() 函数

int pthread_mutex_destroy(pthread_mutex_t *mutex);

注意:
静态分配的互斥量不需要销毁。
不要销毁一个已经加锁的互斥量。
已经销毁的互斥量要保证后面不会有线程再尝试加锁。

申请锁资源

pthread_mutex_lock() 函数

int pthread_mutex_lock(pthread_mutex_t *mutex);

函数以阻塞方式申请互斥锁
互斥锁处于未加锁状态,函数会将互斥量锁定,同时返回成功。
如果互斥锁处于加锁状态,或者未能竞争到锁资源,则函数陷入阻塞,等待互斥锁解锁。

非阻塞方式申请互斥锁的函数为

int pthread_mutex_trylock(pthread_mutex_t *mutex);
释放锁资源

pthread_mutex_unlock()

int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0,否则返回指明错误的错误编号。

释放锁资源只能由占有该互斥锁的线程完成。

现在我们将前面的程序进行修改
这里写图片描述

这里写图片描述

条件变量

前面介绍的互斥锁可以解决资源的互斥访问,但并不完美。
例如:我们有一个全局变量 i,j 线程 A 和 B都要访问,线程A需要互斥的执行i++,j–,线程 B 需要互斥的在i == j 的时候执行 do()操作。
此时如果只是用互斥锁 ,可能导致 do() 永远执行不到。

分析:
1. 线程 A 抢占到互斥锁,执行操作,完成释放互斥锁。
2. 线程 A 和 B 都有可能抢占到锁,如果 B 抢占到,条件不满足,退出。
如果 A 抢占到 ,则执行操作。
整体来看,在整个程序中线程 B 仅仅只有一种情况需要执行 do() 操作。但是 B 有可能会争取到锁,不停的申请和释放互斥锁将造成资源的浪费。

所以此时我们就需要用到条件变量。
我们对上面的代码进行改进:
如果线程 B 抢占到互斥锁,在 i != j 的 情况下,释放互斥锁,使线程等待该条件变量。
如果线程 A 抢占到互斥锁,在 i != j 时继续执行,而在条件变量满足时释放互斥锁。并通知线程 B,从而使 do() 得以执行,提高了访问效率。

条件变量不能单独使用,需要配合互斥锁一起实现对资源的互斥访问。

操作函数

初始化

动态分配:
pthread_cond_init() 函数
在使用条件变量前,需要定义条件变量
pthread_cond_t condtion;

int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
第一个参数为只想要初始化或损坏的条件变量的指针
第二个参数为指向属性对象的指针

静态分配:
同互斥量相同:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

销毁

pthread_cond_destroy()函数

int pthread_cond_destroy(pthread_cond_t *cond);

通知等待的条件变量

pthread_cond_signal() 函数

int pthread_cond_signal(pthread_cond_t *cond);
用来通知等待条件变量的第一个线程
如果 cond 上没有阻塞任何线程,则函数不起作用。

int pthread_cond_broadcast(pthread_cond_t *cond);
用来通知等待条件变量的所有线程

等待条件变量

pthread_cond_wait() 函数

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
第一个参数指向要等待的条件变量
第二个参数指向与条件变量 cond 相关联的互斥锁的指针
如果某线程因等待条件变量进入等待状态时,将隐含释放其申请的互斥锁。
在返回时,隐含申请到该互斥锁的操作。
基于条件变量的生产者消费者模型

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

信号量

此时我们介绍的信号量是 POSIX 信号量,前面进程间通信所用到的信号量是 SYSTEM V 信号量,不同的是 SYSTEM V中的信号量只能用于进程间同步。

初始化

sem_init() 函数

int sem_init(sem_t *sem, int pshared, unsigned int value);
第一个参数和前面的互斥量,条件变量相同
第二个参数 pshared == 0 表示在线程间共享
         pshared != 0 表示在进程间共享
第三个参数 信号量初始值

销毁

sem_destroy()函数

int sem_destroy(sem_t *sem);

等待信号量

sem_wait() 函数

int sem_wait(sem_t *sem);
等待信号量,会将信号量 -1

发布信号量

sem_post()

int sem_post(sem_t *sem);
发布信号量,会将信号量 +1

读写锁

当我们在对数据的读写操作时,很多情况下是大量的读操作,而少量的写操作。
显然,此时使用互斥锁也会极大的影响性能。
此时读写锁机制就可以用来处理这种读多写少的情况

读写锁机制

  1. 如果当前线程读数据,则允许其他线程执行读操作,但不允许写操作。
  2. 如果当前线程写数据,其他线程的读,写操作均不允许。

操作

定义全局变量

pthread_rwlock_t rwlock;
初始化/销毁

pthread_rwlock_init()函数

int pthread_rwlock_init(pthread_rwlock_t rwlock, 
                        const pthread_rwlockattr_t* restrict attr);
第一个参数指向要初始化的读写锁指针
第二个参数指向属性对象的指针

静态初始化

PHTREAD_RWLOCK_INITIALIZER 宏 进行静态初始化

pthread_rwlock_destroy() 函数销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

函数成功返回 0 ,否则返回错误编号。

申请读锁

pthread_rwlock_rdlock() 函数

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
函数以阻塞方式来申请读锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
函数以非阻塞方式来申请读锁 
申请写锁

ptherad_rwlock_wrlock()函数

int pthread_rwlock_wrlock(pthread_lock_t *rwlock);
函数以阻塞方式来申请写锁
int phtread_rwlock_trywrlock(pthread_lock_t *rwlock);
函数以非阻塞方式来申请写锁
解锁

pthread_rwlock_unlock()

ptherad_rwlock_unlock(pthread_rwlock_t *rwlock);
如果用来释放读锁,但当前还有其他读锁定时,则保持读锁定状态。
如果释放的是最后一个读锁或写锁,则读写锁将处于解锁状态。
成功返回 0 ,否则返回错误编号并指明错误

我们先验证读锁的并发读
这里写图片描述
这里写图片描述
这里写图片描述

这里写图片描述

我们来验证读写锁同时存在的情况。
这里写图片描述
这里写图片描述
这里写图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值