0
点赞
收藏
分享

微信扫一扫

第三十二篇,互斥锁和读写锁的概念,函数接口与操作pthread_mutex_init()pthread_mutex_lock()pthread_mutex_unlock()pthread_mutex_

艾晓雪 2022-04-03 阅读 120
linuxc语言

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

2、关于互斥锁的函数接口。
1)定义互斥锁变量。    数据类型: pthread_mutex_t
   pthread_mutex_t m;

2)如何初始化互斥锁?  -->  pthread_mutex_init()   --> man 3 pthread_mutex_init
静态初始化:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;   (把宏赋值给互斥锁变量,就已经完成了初始化了)

动态初始化:
  #include <pthread.h>

 int pthread_mutex_init(pthread_mutex_t *restrict mutex,
           const pthread_mutexattr_t *restrict attr);

参数:
    mutex:互斥锁变量的地址
    attr:普通属性设置为NULL

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

3)上锁。   --> pthread_mutex_lock()   --> man 3 pthread_mutex_lock

  #include <pthread.h>

  int pthread_mutex_lock(pthread_mutex_t *mutex);

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

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


4)解锁。  -->  pthread_mutex_unlock()  --> man 3 pthread_mutex_unlock

    #include <pthread.h>

   int pthread_mutex_unlock(pthread_mutex_t *mutex);

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

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

5)销毁互斥锁。   -->  pthread_mutex_destroy()  --> man 3 pthread_mutex_destroy

   #include <pthread.h>

  int pthread_mutex_destroy(pthread_mutex_t *mutex);

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

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

什么情况下会使用互斥锁?
当多个线程使用同一个资源时,为了防止多个线程一起访问该资源,我们一般会这么做:

   上锁
   .....
   .....   -> 开始做任务
   .....
   解锁


     练习1: 使用互斥锁去完成昨晚作业的第2题。


#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;  //静态初始化

void *func(void *arg)
{
    //上锁
    pthread_mutex_lock(&m);
    
    //做任务
    char str[] = "helloworld";
    
    int i;
    for(i=0;str[i]!='\0';i++)
    {
        fprintf(stderr,"%c",str[i]);
        sleep(1);
    }
    
    //解锁
    pthread_mutex_unlock(&m);
    
    pthread_exit(NULL);
}

int main(int argc,char *argv[])
{
    int i;
    pthread_t tid[5];
    for(i=0;i<5;i++)
    {
        pthread_create(&tid[i],NULL,func,NULL);
    }
    
    for(i=0;i<5;i++)
    {
        pthread_join(tid[i],NULL);
    }
    
    pthread_mutex_destroy(&m);
    
    return 0;
}

二、读写锁。   
1、互斥锁有什么缺陷?
互斥锁无论是读操作还是写操作,都是要先上锁,那么这样效率就会非常低,因为读取数据就没必要分开读,可以一起读。
最好办法: 访问资源可以一起访问,但是修改资源就不可以一起修改。

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

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

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

2)初始化读写锁?   --> pthread_rwlock_init()  --> man 3 pthread_rwlock_init
静态初始化:
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;

动态初始化:
    #include <pthread.h>

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

参数:
    rwlock: 读写锁的变量的地址
    attr:属性设置为NULL即可。

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

3)上读锁。   --> pthread_rwlock_rdlock()  --> man 3 pthread_rwlock_rdlock

   #include <pthread.h>

   int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

参数:
    rwlock:读写锁的变量的地址

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


4)上写锁。   -->  pthread_rwlock_wrlock()  --> man 3 pthread_rwlock_wrlock


   #include <pthread.h>

  int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

参数:
    rwlock:读写锁的变量的地址

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

5)解锁。   -->  pthread_rwlock_unlock()  -> man 3 pthread_rwlock_unlock
    
   #include <pthread.h>

    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

参数:
    rwlock:读写锁的变量的地址

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

6)销毁读写锁  --> pthread_rwlock_destroy()  -> man 3 pthread_rwlock_destroy    

  #include <pthread.h>

  int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

参数:
    rwlock:读写锁的变量的地址

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

    练习2:  临时资源: "int a = 100"   --> 全局变量
        现在有4个线程,有两个线程想打印a的值  --> 读   t1:3s    t2:5s
                      有两个线程想修改a的值  一个想修改为100   一个想修改为50
                             t3:4s     t4:6s

    验证:  1)读锁可以同时上,但是写锁不能同时上。
        2)读锁与写锁能不能同时上?   --> 不可能

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

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

//读
void *func1(void *arg)
{
    //上读锁
    pthread_rwlock_rdlock(&rwlock);
    printf("thread1 read lock!\n");
    
    printf("a = %d\n",a);  //打印a的值
    sleep(3);
    
    //解锁
    pthread_rwlock_unlock(&rwlock);
    printf("thread1 read unlock!\n");
}

//读
void *func2(void *arg)
{
    //上读锁
    pthread_rwlock_rdlock(&rwlock);
    printf("thread2 read lock!\n");
    
    printf("a = %d\n",a);  //打印a的值
    sleep(5);
    
    //解锁
    pthread_rwlock_unlock(&rwlock);
    printf("thread2 read unlock!\n");    
}

//写
void *func3(void *arg)
{
    //上写锁
    pthread_rwlock_wrlock(&rwlock);
    printf("thread3 write lock!\n");
    
    a = 100;
    sleep(4);
    
    //解锁
    pthread_rwlock_unlock(&rwlock);
    printf("thread3 write unlock!\n");
}

//写
void *func4(void *arg)
{
    //上写锁
    pthread_rwlock_wrlock(&rwlock);
    printf("thread4 write lock!\n");
    
    a = 50;
    sleep(6);
    
    //解锁
    pthread_rwlock_unlock(&rwlock);
    printf("thread4 write unlock!\n");
}

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

int main(int argc,char *argv[])
{
    //0. 创建一个线程,用于计算时间的流逝
    pthread_t time_test;
    pthread_create(&time_test,NULL,func_time,NULL);
    
    //1. 创建4个线程
    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;
}

三、条件变量。
1、什么是条件变量?
线程因为某一个条件/情况不成立下,就会进入一个变量中等待,这个存储线程的变量就是条件变量。
条件变量一定要与互斥锁连用。

2、关于条件变量的函数接口。
1)定义一个条件变量。   --> 数据类型: pthread_cond_t
   pthread_cond_t v;

2)初始化条件变量。   -->  pthread_cond_init()   --> man 3 pthread_cond_init
静态初始化:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

动态初始化:
    #include <pthread.h>

   int pthread_cond_init(pthread_cond_t *restrict cond,
           const pthread_condattr_t *restrict attr);

参数:
    cond:条件变量的地址
    attr:普通属性设置为NULL

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

3)如何进入条件变量中等待?  --->  pthread_cond_wait()  -> man 3 pthread_cond_wait
进入条件变量时,会自动解锁。

    #include <pthread.h>

   int pthread_cond_wait(pthread_cond_t *restrict cond,
           pthread_mutex_t *restrict mutex);

参数:
    cond:条件变量的地址
    mutex:互斥锁的地址

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

4)如何唤醒条件变量中的线程?
离开条件变量时,会自动上锁。

广播:唤醒所有在条件变量中的线程。      --> pthread_cond_broadcast()
单播:随机唤醒条件变量中其中一条线程。  --> pthread_cond_signal()

    #include <pthread.h>

   int pthread_cond_broadcast(pthread_cond_t *cond);
   int pthread_cond_signal(pthread_cond_t *cond);

参数:
    cond:条件变量的地址

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

5)如何销毁条件变量?   --> pthread_cond_destroy()  --> man 3 pthread_cond_destroy

   #include <pthread.h>

  int pthread_cond_destroy(pthread_cond_t *cond);

参数:
    cond:条件变量的地址

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

    练习4: 有5个线程,每一个线程任务都是拿200块。先在银行卡(全局变量sum)中存400块,2个线程拿钱退出了,3个进入条件变量中睡眠。
        主线程5s后再打400块到银行卡,唤醒所有的小孩起来拿钱,4个线程退出了,1个还在条件变量睡眠,3s后再打200块,然后唤醒一个线程起来拿钱
        5个线程都可以拿到钱退出。


#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

int sum = 400;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;  //互斥锁静态初始化
pthread_cond_t v = PTHREAD_COND_INITIALIZER;  //条件变量静态初始化

//每一个子线程任务:都是从银行卡中取200块。
void *func(void *arg)
{
    //1. 访问银行卡之前,都要上锁
    pthread_mutex_lock(&m);
    
    //2. 判断银行卡中是否<200块
    while(sum < 200)
    {
        //3. 如果<200,那么就进入条件变量中等待
        pthread_cond_wait(&v,&m);
    }
    
    //4. 代码能运行到这里,sum肯定是>=200
    
    //5. 拿钱
    printf("before money:%d\n",sum);
    sum -= 200;
    printf("after money:%d\n",sum);
    
    //6. 解锁
    pthread_mutex_unlock(&m);
    
    //7. 走人
    pthread_exit(NULL);
}

void *time_func(void *arg)
{
    int i;
    for(i=0;i<100;i++)
    {
        printf("i = %d\n",i);
        sleep(1);
    }
}

int main(int argc,char *argv[])
{
    //0. 创建一个线程,用于计算时间的流逝
    pthread_t tid_test;
    pthread_create(&tid_test,NULL,time_func,NULL);
    
    //1. 创建5个线程
    int i;
    pthread_t tid[5];
    for(i=0;i<5;i++)
    {
        pthread_create(&tid[i],NULL,func,NULL);
    }
    
    //2. 往银行卡中打钱
    sleep(5);
    pthread_mutex_lock(&m);
    sum += 400;
    printf("main thread + 400!\n");
    pthread_mutex_unlock(&m);
    
    //3. 唤醒所有的线程
    sleep(2);
    pthread_cond_broadcast(&v);
    sleep(3);
    
    //4. 再打200块
    pthread_mutex_lock(&m);
    sum += 200;
    printf("main thread + 200!\n");
    pthread_mutex_unlock(&m);
    
    //5. 唤醒一个小孩
    sleep(2);
    pthread_cond_signal(&v);
    
    //6. 接合所有的线程
    for(i=0;i<5;i++)
    {
        pthread_join(tid[i],NULL);
    }
    
    //7. 销毁互斥锁与条件变量
    pthread_mutex_destroy(&m);
    pthread_cond_destroy(&v);
    
    return 0;
}


 

举报

相关推荐

0 条评论