基础知识
- 同步的必要性:对共享资源的竞争,并发
- 实现同步的方法很多:互斥锁、读写锁、条件变量以及信号量等
锁
互斥锁mutex
本质是结构体,里面有整数,也有阻塞队列等
pthread_mutex_t lock;//创建锁
pthread_mutex_init(&lock,NULL);//设置锁为1
pthread_mutex_lock(&lock);//--,如果<0则加入阻塞队列
pthread_mutex_unlock(&lock);//++,如果<=0,则从阻塞队列取出线程
pthread_mutex_destroy(&lock);//销毁锁,释放空间
读写锁rwlock
注意只有一把锁,只是加锁方式有两种,解锁方式统一
- 读共享 :
- 多个读线程可以同时持有读锁。
- 如果当前有写线程正在执行或等待(
active_writers > 0或writers > 0),读线程需要等待。
- 写独占 :
- 同一时间只能有一个写线程持有写锁。
- 如果当前有其他写线程或读线程正在执行(
active_writers > 0或readers > 0),写线程需要等待。
- 写优先:
- 写线程释放锁时,优先唤醒其他写线程(避免写线程饥饿)。
- 如果没有写线程等待,才唤醒所有读线程。
pthread_rwlock_t rwlock; // 创建读写锁
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock); //读加锁,允许多个读
pthread_rwlock_wrlock(&rwlock); //写加锁
pthread_rwlock_unlock(&rwlock); //解说,读写都统一
pthread_rwlock_destroy(&rwlock);
条件变量
本身不是锁,锁mutex的基础上增加条件等待,只有在其他线程通知或超时的情况下条件才会满足
pthread_cond_t cond;//创建条件变量,内部也有阻塞队列
pthread_cond_init(&cond,NULL);
pthread_cond_wait(&cond,&mutex);
pthread_cond_signal(&cond);//从阻塞队列中拿出至少一个线程
pthread_cond_broadcast(&cond);//将所有线程移除阻塞队列
pthread_cond_destroy(&cond);
重点
- wait过程(消费者):先将mutex解锁并将线程移入cond的阻塞队列中,等待条件满足,条件满足时,会重新加锁;
- signal/broadcast过程(生产者):将至少一个/全部线程移出cond的阻塞队列
- 虚假唤醒:由于可能会将多个消费者线程同时移出条件阻塞队列,而产品只有一个时,就会出现错误,因此消费者需要重复判断是否满足条件:
while (condition_not_met) {
// 必须用while循环检查条件
pthread_cond_wait(&cond, &mutex); // 原子操作:解锁+阻塞
}
- 所有使用条件变量的代码,都必须用
while循环检查条件!
信号量semaphore
能用于进程和线程,相当于初始值为N的互斥量mutex,N表示可以同时访问共享数据的线程数
sem_t sem;
sem_init(&sem,0,5);//允许5个线程同时操作
sem_wait(&sem);//--操作,<0则阻塞
sem_trywait(&sem);//尝试--,小于0则不--
sem_timedwait(&sem);//超时则唤醒,需搭配timespec结构体
sem_post(&sem);//++ ,<=0则唤醒
sem_destroy(&sem);