互斥量(互斥锁)
互斥量的工作原理:当一个线程访问临界资源时,对临界资源加锁,阻止另一个线程访问临界资源,使用完之后再进行解锁。
当两个线程的指令交叉执行,会导致线程同步的问题。互斥量可以保证先后执行,先执行完一个线程的相关指令,再执行另一个的相关指令。(原子性)
原子性:一系列操作不可被中断,要么全部执行完成,要么全部没有执行。
互斥量是最简单的线程同步的方法。
互斥量,处于两态之一的变量:解锁和加锁。两个状态可以保证资源访问的串行。
操作系统直接提供了互斥量的API,开发者可以直接使用API完成资源的加锁、解锁操作。C的API为:pthread_mutex_t。
// 互斥锁的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//临界资源
int num=0;
//生产者
void *producer(void*){
int times = 100000000;
while(time--){
pthread_mutex_lock(&mutex);
num+=1;
pthread_mutex_unlock(&mutex);
}
}
//消费者
void *consumer(void*){
int times = 100000000;
while(time--){
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,&consumer,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("print in main function:num= %d\n",num);
return 0;
}
自旋锁
自旋锁的工作原理:与互斥量相同,也是在使用临界资源时加锁,完成之后解锁释放,保证临界资源串行访问。
自旋锁也是一种多线程同步的变量。
使用自旋锁的线程会反复检查锁变量是否可用。
自旋锁不会让出CPU,是一种忙等待状态。
自旋锁是一种 死循环等待锁被释放 的锁。
自旋锁避免了进程或线程上下文切换的开销。
操作系统内部很多地方使用的是自旋锁。
自旋锁不适合在单核CPU使用。
C的API为:pthread_spinlock_t
// 自旋锁的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_spinlock_t spin_lock;
//临界资源
int num=0;
//生产者
void *producer(void*){
int times = 100000000;
while(time--){
pthread_spin_lock(&spin_lock);
num+=1;
pthread_spin_unlock(&spin_lock);
}
}
//消费者
void *consumer(void*){
int times = 100000000;
while(time--){
pthread_spin_lock(&spin_lock);
num-=1;
pthread_spin_unlock(&spin_lock);
}
}
int main(){
printf("start in main function.");
pthread_spin_init(&spin_lock,0)
pthread_t thread1,thread2;
pthread_create(&thread1,NULL,&producer,NULL);
pthread_create(&thread2,NULL,&consumer,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("print in main function:num= %d\n",num);
return 0;
}
读写锁
当线程对临界资源多读少写时(例如对历史订单数据库,一般只会进行查看,而不会更改),读取的时候并不会改变临界资源的值,使用读写锁可以提高效率。
读写锁是一种特殊的自旋锁。
允许多个读者同时访问资源以提高读性能。
读和写之间是互斥的,读和读之间不互斥。
C的API为:pthread_rwlock_t
// 读写锁的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;
//临界资源
int num=0;
//读操作
void *reader(void*){
int times = 100000000;
while(time--){
pthread_rwlock_rdlock(&rwlock);
if(time%1000==0){
usleep(10)
}
pthread_rwlock_unlock(&rwlock);
}
}
//写操作
void *writer(void*){
int times = 100000000;
while(time--){
pthread_rwlock_wdlock(&rwlock);
num-=1;
pthread_rwlock_unlock(&rwlock);
}
}
int main(){
printf("start in main function.");
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时,不允许消费者消费,消费者必须等待;
缓冲区满时,不允许生产者往缓冲区生产,生产者必须等待。
当生产者生产一个产品时,唤醒可能等待的消费者;
当消费者消费一个产品时,唤醒可能等待的生产者。
条件变量就是实现唤醒操作
C的API为:pthread_cond_t,需要配合互斥量使用。
// 条件变量的例子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <vector>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//临界资源
int MAX_BUF=100;
int num=0;
//生产者
void *producer(void*){
while(true){
pthread_mutex_lock(&mutex);
while(num>=MAX_BUF){
//等待
pthread_cond_wait(&cond,&mutex);
printf("缓冲区满了,等待消费者消费...\n");
}
num+=1;
printf("生产一个产品,当前产品数量为:%d\n",num);
sleep(1);
pthread_cond_signal(&cond);
printf("通知消费者...\n");
pthread_mutex_unlock(&mutex);
}
}
//消费者
void *consumer(void*){
while(true){
while(num<=0){
//等待
pthread_cond_wait(&cond,&mutex);
printf("缓冲区空了,等待生产者生产...\n");
}
num-=1;
printf("消费一个产品,当前产品数量为:%d\n",num);
sleep(1);
pthread_cond_signal(&cond);
printf("通知生产者...\n");
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,&consumer,NULL);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
printf("print in main function:num= %d\n",num);
return 0;
}
线程同步方法总结
互斥锁、自旋锁、读写锁
条件变量