线程同步
数据不一致的主要原因就是多个线程指令交叉执行
互斥量
互斥量可以保证先后执行,保证了关键操作的原子性
操作系统提供了互斥量的API:pthread_mutex_t
#include
#include
#include
#include
#include
// 定义一个互斥量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 临界资源
int num = 0;
// 生产者
void *producer(void*){
int times = 100000000;
while(times --){
// 给互斥量加锁
pthread_mutex_lock(&mutex);
num += 1;
// 给互斥量解锁
pthread_mutex_unlock(&mutex);
}
}
// 消费者
void *comsumer(void*){
int times = 100000000;
while(times --){
pthread_mutex_lock(&mutex);
num -= 1;
pthread_mutex_unlock(&mutex);
}
}
int main(){
printf("Start in main function.");
pthread_t thread1, thread2;
// 创建一个线程
// 线程标识符指针 线程属性 运行函数指针 运行函数参数
pthread_create(&thread1, NULL, &producer, NULL);
pthread_create(&thread2, NULL, &comsumer, NULL);
// 等待一个线程的结束
// 线程标识符 等待线程的返回值
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Print in main function: num = %d\n", num);
return 0;
}
自旋锁
自旋锁和互斥量思想没什么差别
自旋锁会反复检查锁是否可用,并不会让出CPU,其在死循环等待锁被释放,避免了进程后线程上下文切换的开销,但如果加锁时间比较持久,CPU会一直处于空转状态
操作系统提供了自旋锁API:pthread_spinlock_t
#include
#include
#include
#include
#include
// 声明一个自旋锁
pthread_spinlock_t spin_lock;
int num = 0;
void *producer(void*){
int times = 10000000;
while(times --){
// 给自旋锁加锁
pthread_spin_lock(&spin_lock);
num += 1;
// 释放自旋锁
pthread_spin_unlock(&spin_lock);
}
}
void *comsumer(void*){
int times = 10000000;
while(times --){
pthread_spin_lock(&spin_lock);
num -= 1;
pthread_spin_unlock(&spin_lock);
}
}
int main(){
printf("Start in main function.\n");
// 初始化自旋锁
// 自旋锁标识符指针 自旋锁的共享范围(仅初始化本自旋锁的线程所在的进程内的线程才能够使用该自旋锁)
pthread_spin_init(&spin_lock, PTHREAD_PROCESS_PRIVATE);
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, &producer, NULL);
pthread_create(&thread2, NULL, &comsumer, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Print in main function: num = %d\n", num);
return 0;
}
读写锁
临界资源多读少写,读取的时候并不会改变临界资源的值,是一种特殊的自旋锁,允许多个读者同时访问资源,但对于写操作时互斥的
操作系统提供了读写锁API:pthread_rwlock_t
pthread_rwlock_rdlock
pthread_rwlock_wrlock
#include
#include
#include
#include
#include
int num = 0;
// 定义读写锁
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void *reader(void*){
int times = 10000000;
while(times --){
// 加读锁
pthread_rwlock_rdlock(&rwlock);
if (times % 1000 == 0){
printf("print num in reader: num = %d\n", num);
usleep(10);
}
// 释放读锁
pthread_rwlock_unlock(&rwlock);
}
}
void *writer(void*){
int times = 10000000;
while(times --){
// 加写锁
pthread_rwlock_wrlock(&rwlock);
num += 1;
// 释放写锁
pthread_rwlock_unlock(&rwlock);
}
}
int main(){
printf("Start in main function.\n");
pthread_t thread1, thread2, thread3;
pthread_create(&thread1, NULL, &reader, NULL);
pthread_create(&thread2, NULL, &reader, NULL);
pthread_create(&thread3, NULL, &writer, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
printf("Print in main function: num = %d\n", num);
return 0;
}
条件变量
条件变量允许线程睡眠,直到满足某种条件,当满足条件时,可以向该线程发送信号通知唤醒
对于生产者消费者问题来说
缓冲区小于等于0的时候不允许消费者消费,消费者必须等待
缓冲区满时,不允许生产者网缓冲区生产,生产者必须等待
所以当生产者生产一个产品时,唤醒可能等待的消费者;当消费者消费一个产品时,唤醒可能等待的生产者
操作系统提供了条件变量API:pthread_cond_t
pthread_cond_wait
pthread_cond_signal
#include
#include
#include
#include
#include
#include
#include
int MAX_BUF = 100;
int num = 0;
// 定义一个条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 定义一个互斥锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* producer(void*){
while(true){
pthread_mutex_lock(&mutex);
while (num >= MAX_BUF){
// 等待
printf("缓冲区满了, 等待消费者消费...\n");
pthread_cond_wait(&cond, &mutex);
}
num += 1;
printf("生产一个产品,当前产品数量为:%d\n", num);
sleep(1);
pthread_cond_signal(&cond);
printf("通知消费者...\n");
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
void* consumer(void*){
while(true){
pthread_mutex_lock(&mutex);
while (num <= 0){
// 等待
printf("缓冲区空了, 等待生产者生产...\n");
pthread_cond_wait(&cond, &mutex);
}
num -= 1;
printf("消费一个产品,当前产品数量为:%d\n", num);
sleep(1);
pthread_cond_signal(&cond);
printf("通知生产者...\n");
pthread_mutex_unlock(&mutex);
}
}
int main(){
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, &consumer, NULL);
pthread_create(&thread2, NULL, &producer, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}