【Linux】多线程---互斥锁,同步
发布日期:2021-05-10 06:33:34 浏览次数:5 分类:技术文章

本文共 3521 字,大约阅读时间需要 11 分钟。

1.互斥

  • 想要保证互斥,我们需要用到互斥锁
  • 互斥锁本身也是一个资源,或者我们也需要在代码中来获取互斥锁,一定只要多个线程保证互斥,需要都去获取互斥锁,否则就无法保证互斥
     

2.互斥锁

  • 本质:在互斥锁内部当中有一个计数器其实就是互斥量
  • 当线程获取互斥锁的时候,如果计数器当中的值为0,表示当前线程获取不到互斥锁,也就是没有获取互斥锁,就不要在去获取临界资源了
  • 当线程获取互斥锁的时候,如果计数器当中的值为1,表示当前线程可以获取到互斥锁,也就是意味着可以访问到临界资源,代码可以执行临界区当中的代码

 

  • 问题:为什么计数器当中的值从0变成1,或者从1变成0,是原子操作(计数器当中的值如何保证原子性)
    在这里插入图片描述
  • 获取锁资源的时候(加锁)
    寄存器当中的值直接赋值成为0
    ② 将寄存器当中的值和计数器当中的值进行交换
    ③ 判断寄存器当中的值,得出加锁的结果
     
    在这里插入图片描述
    当寄存器当中的值为1的时候,则表示可以加锁
    当寄存器当中的值为0的时候,则表示不可以加锁

 

3.互斥锁的接口

  • 初始化互斥锁变量
  • 动态互斥锁
  • pthread_mutex_t :互斥锁变量的类型
  • int pthread_mutex_init(pthread_mutex_t * mutex, pthread_mutexattr_t *attr)
      mutex:传入互斥锁变量的地址
      pthread_mutex_init会初始化互斥锁变量
      attr:属性,一般传递NULL
    eg:pthread_mutex_t lock;
      pthread_mutex_init(&lock, NULL);

 

  • 静态互斥锁
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     
  • 加锁
  • int pthread_mutex_lock(pthread_mutex_t *mutex); // 阻塞加锁的接口
  • mutex:传入互斥锁变量的地址
     
      如果mutex当中的计数器的值为1,则pthread_mutex_lock接口就返回了,表示说加锁成功,同时计数器当中的值会被更改为0
      如果mutex当中的计数器的值返回为0,则pthread_mutex_lock接口被阻塞了,pthread_mutex_lock接口没有返回,阻塞在该函数的内部,直接加锁成功
     
     
  • int pthread_mutex_trylock(pthread_mutex_t *mutex)
  • 非阻塞加锁接口
  • 含义
    当互斥锁变量的计数器置为1,则加锁成功返回
    当互斥锁变量当中的计数器置为0,也会返回,但是一定要清楚,加锁并没有成功,也就是不要去访问临界资源
    一般我们非阻塞接口都需要配合循环来使用
     
    &nsbp;
  • 带有超时时间的加锁接口
  • int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout);
  • 带有超时时间的接口,也就是意味着当不能直接获取互斥锁的时候,会等待abs_timeout时间
    如果在这个时间内部加锁成功了,直接返回,不需要在继续等待剩余的时间,并且表示加锁成功
    如果超过该事件,也返回掉了,但是表示加锁失败了,需要循环加锁
     
  • 解锁
  • int pthread_mutex_unlock(pthread_mutex_t * mutex)
  • 不管是哪一个加锁接口成功的,都可以使用该接口进行解锁
  • 解锁的时候,会讲互斥锁变量当中的计数器的值,从0变成1,表示其他线程可以获取互斥锁
     
  • 销毁互斥锁
  • pthread_mutex_destory(pthread_mutex_t *);
     

4.同步

  • 同步为了保证各个线程对临界资源访问的合理性
  • 条件变量:PCB等待队列+一堆接口(等待接口+唤醒接口)
    在这里插入图片描述
     

5.条件变量的接口

  • 初始化
  • int pthread_cond_init(pthreaed_cond_t* cond, pthread_condattr_t* attr)
    cond:pthread_cond_t:条件变量的类型,传参的时候还是传入条件变量的地址
    attr:条件变量的属性,通常传递NULL,采用默认属性
     
  • 等待:将调用该接口的线程放到PCB等待队列当中
  • int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);
    cond:条件变量
    mutex:互斥锁
  • 思考:
     
  • 唤醒
  • int pthread_cond_signal(pthread_cond_t* cond)
    作用:通过PCB等待队列当中的线程,将其从队列当中出来,唤醒该线程(唤醒至少一个PCB等待队列当中的线程)
  • int pthread_cond_broadcast(pthread_cond_t * cond)
    唤醒PCB等待队列当中全部的线程
     
  • 释放
  • int pthread_cond_destory(pthread_cond_t* cond)
#include 
#include
#include
#define THREADCOUNT 2int g_bowl = 0;pthread_mutex_t g_mut;pthread_cond_t g_cond;void* MakeStart(void *arg){
while(1) {
pthread_mutex_lock(&g_mut); while(g_bowl > 0) {
pthread_cond_wait(&g_cond, &g_mut); } g_bowl++; printf("i am make %p, g_bowl: %d\n", pthread_self(), g_bowl); pthread_mutex_unlock(&g_mut); pthread_cond_signal(&g_cond); } return NULL;}void* EatStart(void *arg){
while(1) {
// eat pthread_mutex_lock(&g_mut); while(g_bowl <= 0) {
pthread_cond_wait(&g_cond, &g_mut); } g_bowl--; printf("i am make %p, g_bowl: %d\n", pthread_self(), g_bowl); pthread_mutex_unlock(&g_mut); // 通知等待队列做面的人 pthread_cond_signal(&g_cond); } return NULL;}int main(){
pthread_mutex_init(&g_mut, NULL); pthread_cond_init(&g_cond, NULL); pthread_t prod[THREADCOUNT], cons[THREADCOUNT]; for(int i = 0; i < THREADCOUNT; i++) {
int ret = pthread_create(&prod[i], NULL, MakeStart, NULL); if(ret < 0) {
perror("prod create error!\n"); return -1; } ret = pthread_create(&prod[i], NULL, EatStart, NULL); if(ret < 0) {
perror("cons create error!\n"); return -1; } } for(int i = 0; i < THREADCOUNT; i++) {
pthread_join(prod[i], NULL); pthread_join(cons[i], NULL); } pthread_mutex_destroy(&g_mut); pthread_cond_destroy(&g_cond); return 0;}

转载地址:https://blog.csdn.net/weixin_43967449/article/details/110135504 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【Linux】为什么会有互斥锁?
下一篇:Linux中常用的vim三种模式(普通模式、插入模式、底行模式)

发表评论

最新留言

感谢大佬
[***.8.128.20]2023年11月30日 19时00分16秒