当前位置: 首页 > news >正文

3.16-线程同步

线程同步

  • 同步:即协同步调,按预定的先后次序访问共享数据。
  • 数据同步:指一个线程发出某一功能调用时,没有得到结果前,该调用不返回。同时,其他线程为保证数据一致性,不调用该功能。

数据混乱的原因

  1. 资源共享
  2. 调度随机
  3. 线程之间缺乏必要的同步机制
  • 锁的属性:
    • 系统提供给用户的所有锁全部为”建议锁“,不具备强制性。
    • 如果访问共享数据的线程不拿锁,直接访问共享数据,能访问成功但是数据就会有出现混乱的风险。——拿锁与否完全取决于程序的编写。

互斥量(互斥锁)

pthread_mutex_t mutex;创建互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t  *mu‐
texattr); // 初始化互斥锁

int pthread_mutex_lock(pthread_mutex_t *mutex); // 加锁,如果锁被占用,阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex); // 尝试加锁,不阻塞

int pthread_mutex_unlock(pthread_mutex_t *mutex); // 解锁,唤醒阻塞在此锁上的进程

int pthread_mutex_destroy(pthread_mutex_t *mutex); // 销毁互斥锁

注意事项:

  1. 锁的粒度越小越好(访问共享数据前,加锁,访问共享数据结束后,立即解锁)。
  2. 互斥锁,本质:结构体。我们可以把它当成整数看待,初始值为 1
  3. 加锁:-- 操作。失败:阻塞线程
  4. 解锁:++ 操作
  5. try:尝试加锁。成功:–,失败,设置错误号。
  • 初始化互斥量:
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
pthread_mutex_init(&mut, NULL; // 动态初始化 

死锁

  • 死锁不是锁,是一种错误使用锁的状态。
  • 常见死锁种类:
    1. 对一把锁反复 lock。
    2. 两个线程各自持有一把锁,请求另一把锁。

读写锁

  1. 锁只有一把,可以指定为”读模式“和”写模式“。
  2. 读共享,写独占。
  3. 写锁优先级高。
  • 相较于互斥锁而言,读写锁适合对数据结构读的操作次数远高于写的次数的场景。

操作函数

pthread_rwlock_t rwlock; // 创建读写锁对象
       int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
       int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
           const pthread_rwlockattr_t *restrict attr);
       int 
           pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
       int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

       int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
       int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

  • restrict 关键字:
    • 用来修饰指针标量。被该关键字修饰的指针变量指向的内存操作,只能由本指针完成。

条件变量

  • 条件变量不是锁,通常配合互斥锁使用,也能造成线程阻塞

操作函数

pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化

int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); // 动态

int pthread_cond_signal(pthread_cond_t *cond); // 唤醒阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒阻塞在条件变量上的线程

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); // 等待条件满足
作用:
    1. 阻塞等待条件满足。
    2. 解锁已经加锁成功的互斥量。
    ——1. 2. 为一个原子操作,不可再分。
	...等待...
    3. 当条件满足,函数返回时,重新加锁
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const
struct timespec *abstime); // 超时退出

int pthread_cond_destroy(pthread_cond_t *cond); // 销毁条件变量

信号量

  • 信号量与信号无关
  • 信号量相当于初始值为 N 的互斥量,可以表示同时访问共享数据的线程数。

操作函数

sem_t 

int sem_init(sem_t *sem, int pshared, unsigned int value);
// 动态初始化,没有静态20:用于线程间同步
    非 0:用于进程间同步
参 3:
    信号量的初值 N
返回值:成功:0  失败:-1,errno
int sem_destroy(sem_t *sem);

int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

int sem_post(sem_t *sem);
// 一次调用做一次 ++ 操作。当信号为 N 的时候再次 ++ 会阻塞。
int sem_wait(sem_t *sem);
// 一次调用做一次 -- 操作。当信号为 0 的时候再次 -- 会阻塞。

相关文章:

  • Excel(函数篇):COUNTIF与CONUTIFS函数、SUMIF与SUMIFS函数、ROUND函数、MATCH与INDEX函数、混合引用与条件格式
  • ollama注册自定义模型(safetensors)
  • 基于大模型的分泌性中耳炎全流程预测与治疗管理研究报告
  • 【C++】一文吃透STL容器——list
  • Linux内核实时机制30 - 实时优化方案 - 实时与非实时争抢
  • 谷歌开源多模态大模型 Gemma 3:轻量级与高性能的完美融合
  • 【经验】Orin系列Ubuntu远程桌面:VNC、NoMachine、URDC
  • Java EE(11)——文件I(input)/O(output)
  • 有效的山脉数组 力扣941
  • 使用GoldenGate完成SQLserver到Oracle的数据实时同步
  • C语言之 条件编译和预处理指令
  • 数据结构-树(详解)
  • 麒麟服务器操作系统Sqlite部署手册
  • 神聖的綫性代數速成例題4. 矩陣乘法的充要條件、矩陣運算的定義
  • Matlab 液位系统根据输入和输出信号拟合一阶传递函数
  • 【网络】什么是公共 API(Application Programming Interface)?
  • 发现一个GoVCL的问题
  • 并发基础—三大问题:可见性、原子性、有序性
  • 计算机网络笔记再战——理解几个经典的协议HTTP章4
  • PowerToys:解锁Windows生产力的终极武器
  • 中国难以承受高关税压力?外交部:任何外部冲击都改变不了中国经济基本面
  • 当一群杜克土木工程毕业生在三四十年后怀念大学的历史课……
  • 德国巴斯夫:关税政策加剧全球市场不确定性,间接影响已显现
  • 出现这几个症状,说明你真的老了
  • 张建华评《俄国和法国》|埃莲娜·唐科斯的俄法关系史研究
  • 山西太原一小区发生爆炸,太原:进一步深刻汲取教训