线程的同步与互斥

同步与互斥

  1. 同步概念
    程序按一定顺序访问临界资源,如:生产者消费者模型…(先生产再消费)

  2. 互斥概念
    任何时刻,都只能有一个执行流访问临界资源,如:消费者与消费者…(不能同时消费同一个物品)

  3. 引入:
    在进程中,多个线程之间共享进程资源,线程与线程之间便都具有操作该进程资源(临界资源)的能力和权限,为了保护该进程资源(临界资源),便引入了线程的同步与互斥。

互斥量(mutex)

  1. 在进程当中,有很多变量需要各线程共享访问(共享变量),通过共享变量来实现线程间的通信,但是,线程并发的操作该变量会导致出现某些问题;

  2. 例如:有100个食物等待被买走,每个线程每次买走一个物品(买食物前,考虑1秒),总共有六个线程,当不对线程做任何格外的操作时,对于食物而言,每次都会有六个线程同时去买(即:六个线程并发执行),即每次食物减少6个,当食物只剩下4个时,对于六个线程而言,都可以购买食物,但是购买完之后,会发现有两个线程购买了食物0和食物-1;这也就导致了该购买过程是有问题的。

  3. 测试代码:https://gitee.com/free_xin/codes/j307ltvnbz9rxpi58ohde15

  4. 测试结果:
    线程的同步与互斥_第1张图片

  5. 该问题的本质就是:在同一时刻中,多个线程同时访问了一个临界资源,最后导致该临界资源出现问题;也就是说,在多线程访问临界资源时,必须使这些线程之间互斥。

  6. 互斥量的概念
    为了实现线程间的互斥,使得
    1.当有线程在临界区执行时,不允许其他线程进入临界区
    2.当多个线程同时访问临界区,并且临界区没有其他线程执行时,只能使得一个线程进入临界区,其他线程等待或者退出。
    3.当某个线程不在临界区执行时,这个线程不能阻止其他线程进入该临界区

    其本质就是一把锁,这把锁就叫互斥量。

  7. 操作:在要进入临界区之前加锁,在临界区执行结束之后解锁

  8. 互斥量的接口函数
    1.初始化
    a).静态分配
    pthread_mutex_t mutex = PTHREA_MUTEX_INITIALIZER;(设置全局或静态变量,用宏对互斥量进行初始化)
    该互斥量不用自己销毁,系统会维护该互斥量
    b).动态分配
    int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restict attr);
    mutex:要初始化的互斥量
    attr:初始化属性,默认为NULL
    2.销毁
    int pthread_mutex_destroy(pthread_mutex_t *mutex);
    mutex:要销毁的互斥量
    注:已经加锁的互斥量不能销毁;已经销毁的互斥量不要再对其进行加锁;静态分配的互斥量不需要销毁
    3.加锁与解锁
    int pthread_mutex_lock(pthread_mutex_t *mutex); //加锁
    int pthread_mutex_unlock(pthread_mutex_t *mutex); //解锁
    成功返回0,失败返回错误号
    注:对与调用未解锁的互斥量的线程,若线程对其进行加锁,那么该线程会阻塞,直到该互斥量被解锁
    注:因为互斥量本身也是临界资源,所以为了保护自身,加锁操作均是原子操作,解锁操作不是原子操作
    加锁之后的测试代码:https://gitee.com/free_xin/codes/j307ltvnbz9rxpi58ohde15
    加锁之后的测试结果:
    线程的同步与互斥_第2张图片

条件变量

  1. 在了解条件变量之前,先了解一下生产者消费者模型
    生产者消费者模型
    1.一个交易环境(一个进程)
    2.两个对象(生产者(可多个)(给物品进行+1操作),消费者(可多个)(给物品进行-1操作))
    3.三种关系
    a).生产者与生产者:互斥关系
    生产者之间以互斥的方式访问物品资源,否则会造成物品超过上限的情况
    b).生产者与消费者:同步且互斥关系
    同步:在物品数量为0的情况下,必须要先进行生产操作,再进行消费操作。
    互斥:在消费和生产同时访问到物品资源时,会导致资源被错误修改的情况
    例如:在物品资源为50时,消费和生产同时访问物品资源,同时将50这个数据加载到他们各自的寄存器当中,各自修改之后,若消费者先将数据放回内存,那么生产者返回之后的数据是51,若生产者先将数据放回内存,那么消费者返回之后的数据是49,然而实际的数据应该是50,也就造成了数据的错误修改。
    如图:线程的同步与互斥_第3张图片

c).消费者与消费者:互斥关系
生产者之间以互斥的方式访问物品资源,否则会造成物品低于下限的情况

  1. 条件变量概念
    实现线程同步的方式,在条件不满足的时候阻塞等待,在条件满足之后,采用不同的两种方式通知其等待队列当中的线程去获取互斥量,然后进行对临界资源的操作。

  2. 条件变量接口函数
    1.初始化
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    cond:要初始化的条件变量
    attr:初始化属性,默认NULL
    2.销毁
    int pthread_cond_destroy(pthread_cond_t *cond);
    3.等待(阻塞,条件满足之后执行该接口之后的临界区)
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
    cond:要等待的条件变量
    mutex:要获取以及释放的互斥量
    等待实质(如图)线程的同步与互斥_第4张图片
    4.唤醒等待
    a).惊群唤醒(一次性直接将等待队列上的所有的线程唤醒)
    int pthread_cond_broadcast(pthread_cond_t *cond);
    b).一个个唤醒
    int pthread_cond_signal(pthread_cond_t *cond);
    注:条件变量的使用必须与互斥量搭配使用
    测试代码(模拟实现生产者消费者模型):https://gitee.com/free_xin/codes/0t7k9l5vbwnidsx3or4qm96

POSIX信号量

  1. 本质上是一个用来描述临界资源的计数器,也是一个临界资源,为了保护自身,其P、V操作都具有原子性
    和进程间通信的(SystemV)信号量相同,都是用于同步操作
    ,这里的信号量用于线程同步。

  2. 关于信号量的其他特征,可以参考SystemV信号量:https://blog.csdn.net/Code_ZX/article/details/85008036

  3. 实现同步的方式
    在生产者消费者模型中,设置两个信号量,分别为货品数量,空位数量,生产者负责对空位数信号量-1,对货品数信号量+1,消费者负责对货品数信号量-1,对空位数信号量+1;当货品数信号量为0时,阻塞,此时执行生产者,当执行生产者的线程执行完毕或者货品数量达到上限,唤醒消费者…(一直循环)
    图解:线程的同步与互斥_第5张图片

  4. 信号量接口函数(包含头文件:
    1.初始化
    int sem_init(sem_t sem, int pshared, unsigend int value);
    sem:要初始化的信号量
    pshared:0表示该信号量在线程间共享,非0表示该信号量在进程间共享
    value:信号量初值
    2.销毁
    int sem_destroy(sem_t *sem);
    3.等待(对信号量进行-1操作,P操作)
    int sem_wait(sem_t *sem);
    4.发布(对信号量进行+1操作,V操作)
    int sem_post(sem_t *sem);
    测试代码:https://gitee.com/free_xin/codes/zp2sxike01h5locvmbwt636

读写锁

  1. 读者写者模型:写者修改共享资源,读者不对共享资源进行修改读者的数量远大于写者的数量,在读者写者模型当中,一般就采用读写锁。
    概念
    其本质上是一种自旋锁(等待时间短),其中写者独占,读者共享,读者和写者同时访问时,写者优先

  2. 接口函数
    1.初始化
    int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
    2.销毁
    int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    3.加锁
    a).加读者锁
    int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    b).加写者锁
    int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    4.解锁
    int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
    注:适用于读者写者模型

欢迎各位莅临指导…
xiexie…
线程的同步与互斥_第6张图片

你可能感兴趣的:(学习笔记)