线程同步
- 同步:即协同步调,按预定的先后次序访问共享数据。
- 数据同步:指一个线程发出某一功能调用时,没有得到结果前,该调用不返回。同时,其他线程为保证数据一致性,不调用该功能。
数据混乱的原因
- 资源共享
- 调度随机
- 线程之间缺乏必要的同步机制
- 锁的属性:
- 系统提供给用户的所有锁全部为”建议锁“,不具备强制性。
- 如果访问共享数据的线程不拿锁,直接访问共享数据,能访问成功但是数据就会有出现混乱的风险。——拿锁与否完全取决于程序的编写。
互斥量(互斥锁)
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
- 加锁:-- 操作。失败:阻塞线程
- 解锁:++ 操作
- try:尝试加锁。成功:–,失败,设置错误号。
- 初始化互斥量:
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; // 静态初始化
pthread_mutex_init(&mut, NULL; // 动态初始化
死锁
- 死锁不是锁,是一种错误使用锁的状态。
- 常见死锁种类:
- 对一把锁反复 lock。
- 两个线程各自持有一把锁,请求另一把锁。
读写锁
- 锁只有一把,可以指定为”读模式“和”写模式“。
- 读共享,写独占。
- 写锁优先级高。
- 相较于互斥锁而言,读写锁适合对数据结构读的操作次数远高于写的次数的场景。
操作函数
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);
// 动态初始化,没有静态
参 2:0:用于线程间同步非 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 的时候再次 -- 会阻塞。