[Linux系统编程]线程同步(五)

       距离上一次利用高并发技术实现360度行车记录仪功能已经过去半年了。开始写一系列关于系统编程和网络编程内容进行总结。
       温故而知新,欢迎大家讨论学习。

2021-09-08 复习内容

  • 复习代码
  • 1 man 1 man 2 man 3 分别是标准命令 系统调用 和 库函数
  • 编译需要 -l pthread 加载第三方库
  • 什么叫第三方库呢:除本地类库、系统类库以外的类库,需要后来安装,才能调用的类库。C++常见第三方库参考
  • 信号量那边的代码 如果是多生产者 都消费者就是错误的,因为没有处理生产者和生产者 或者 消费者和消费者之间互斥访问的问题。可能存在同时进入临界区。
  • sem_init 初始化 分线程间 参数为0 进程间 参数为1 注意

2021-10-06 补充内容

  • 条件变量虚假唤醒的问题
  • 条件变量也叫做管程
  • 生产者生产 上锁 生产 解锁 唤醒 ;消费者消费 上锁 while 判断阻塞 消费 释放锁。

1 同步的概念

  • 所谓同步,即同时起步,协调一致。不同的对象,对“同步”的理解方式略有不同。
  • 设备同步,是指在两设备之间规定一个共同的时间参考;
  • 数据库同步,是指让两个或多个数据库内容保持一致,或者按需要部分保持
    一致;
  • 文件同步,是指让两个或多个文件夹里的文件保持一致。等等

2 线程同步的概念

  • 线程同步简单说就是线程排队
  • 线程同步是一种制约关系,一个线程的执行依赖另一个线程消息。
    当它没有得到另一个线程的消息时应等待,得到消息被唤醒
  • 线程同步使得多个线程协调工作从而带到一致性

3 为什么要有线程同步

  • 共享资源,多个线程都可对共享资源操作 [容易产生冲突]
  • 线程操作共享资源的先后顺序不确定
  • cpu处理器对存储器的操作一般不是原子操作

举个例子
       两个线程都把全局变量增加1,这个操作平台需要三条指令完成
从内存读到寄存器 →寄存器的值加1 →将寄存器的值写会内存。
      如果此时线程A取值在寄存器修改还未写入内存,线程B就从内存取值就会导致两次操作实际上只修改过一次。或者说后一次线程做的事情覆盖前一次线程做的事情。实际上就执行过一次线程。

4 互斥量 mutex

4.1 基本概念

  • Linux 中提供一把互斥锁 mutex(也称之为互斥量)。
  • 每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
  • 资源还是共享的,线程间也还是竞争的,但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
    在这里插入图片描述

4.2 函数使用

4.2.1 pthread_mutex_t 类型

  • 其本质是一个结构体。为简化理解,应用时可忽略其实现细节,简单当成整数看待。
  • 变量 mutex 只有两种取值 1、0。

4.2.2 pthread_mutex_init 函数

作用
初始化一个互斥锁—> 初值可看作 1

  • int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)
  • 参 1:传出参数,调用时应传 &mutex
  • 参 2:互斥量属性。是一个传入参数,通常传 NULL,选用默认属性(线程间共享)。
4.2.2.1静态初始化和动态初始化
  1. 静态初始化:如果互斥锁 mutex 是静态分配的(定义在全局,或加了 static 关键字修饰),可以直接使用宏进行初始化。e.g. pthead_mutex_t muetx = PTHREAD_MUTEX_INITIALIZER;
  2. 动态初始化:局部变量应采用动态初始化。e.g. pthread_mutex_init(&mutex, NULL)

4.2.3 pthread_mutex_destroy 函数

作用
销毁一个互斥锁

  • int pthread_mutex_destroy(pthread_mutex_t *mutex)

4.2.4 pthread_mutex_lock 函数

作用
加锁。可理解为将 mutex–(或 -1),操作后 mutex 的值为 0。

  • int pthread_mutex_lock(pthread_mutex_t *mutex);

4.2.5 pthread_mutex_unlock 函数

作用
解锁。可理解为将 mutex ++(或 +1),操作后 mutex 的值为 1。

  • int pthread_mutex_unlock(pthread_mutex_t *mutex);

4.2.6 pthread_mutex_trylock 函数

作用
尝试加锁

  • int pthread_mutex_trylock(pthread_mutex_t *mutex);

4.3 加锁和解锁

4.3.1 lock 与 unlock:

  • lock 尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。
  • unlock 主动解锁函数,同时将阻塞在该锁上的所有线程全部唤醒,至于哪个线程先被唤醒,取决于优先级、调度。默认:先阻塞、先唤醒。
  • 例如:T1 T2 T3 T4 使用一把 mutex 锁。T1 加锁成功,其他线程均阻塞,直至 T1 解锁。T1 解锁后,T2 T3 T4 均被唤醒,并自动再次尝试加锁。
  • 可假想 mutex 锁 init 成功初值为 1。lock 功能是将 mutex--。而 unlock 则将 mutex++

4.3.2lock 与 trylock:

  • lock 加锁失败会阻塞,等待锁释放。
  • trylock 加锁失败直接返回错误号(如:EBUSY),不阻塞

5 读写锁

这部分内容在项目中没有使用过,如有需要,下次补充…

6 条件变量

6.1 基本概念

  • 条件变量本身不是锁!但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。

6.2 为什么使用条件变量

  • 线程抢占互斥锁时,线程A抢到了互斥锁,但是条件不满足,线程A就会让出互斥锁让给其他线程,然后等待其他线程唤醒他;一旦条件满足,线程就可以被唤醒,并且拿互斥锁去访问共享区。经过这中设计能让进程运行更稳定。

6.3 函数使用

6.3.1 pthread_cond_init 函数

作用
初始化一个条件变量

  • int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
  • 参 2:attr 表条件变量属性,通常为默认值,传 NULL 即可
6.3.1.1 静态初始化和动态初始化

静态初始化

  • pthread_cond_t cond=PTHREAD_COND_INITIALIZER
    动态初始化
  • int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);

6.3.2 pthread_cond_destroy 函数

作用
销毁一个条件变量

  • int pthread_cond_destroy(pthread_cond_t *cond);

6.3.3 pthread_cond_wait 函数(重)

作用:(非常重要 三点

  1. 阻塞等待条件变量 cond(参 1)满足
  2. 释放已掌握的互斥锁(解锁互斥量)相当于 pthread_mutex_unlock(&mutex);
  3. 当被唤醒,pthread_cond_wait 函数返回时,解除阻塞并重新申请获取互斥锁 pthread_mutex_lock(&mutex);
  • int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);

1.2.两步为一个原子操作。

6.3.4 pthread_cond_timedwait 函数

作用
限时等待

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Windalove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值