线程同步

操作系统编程--线程同步问题

1. 互斥量

  • 互斥量是最简单的线程同步的方法

  • 互斥量(互斥锁),处于两态之一的变量:解锁和加锁

  • 两个状态可以保证资源访问的串行

  • 操作系统直接提供了互斥量的API

  • 开发者可以直接使用API完成资源的加锁、解锁操作

具体操作

◆ pthread_mutex_t //用于定义线程互斥锁对象
◆ pthread_mutex_lock(&mutex) //上锁操作
◆ pthread_mutex_unlock(&mutex) //开锁操作

#include 
#include 
#include 
#include 
#include 

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int num = 0;

void *producer(void*){
    int times = 10000000;
    while(times --){ //不加锁操作则会导致出现共享资源不统一问题 即num 最后不为0
        pthread_mutex_lock(&mutex);
        num += 1;
        pthread_mutex_unlock(&mutex);
    }
}

void *comsumer(void*){
    int times = 10000000;
    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;
}

编译运行

g++ Thread_Mutex.cpp -o Thread_Mutex -lpthread //编译 -lpthread用于连接动态库
./Thread_Mutex //运行

2. 自旋锁

特点:

  • 自旋锁也是一种多线程同步的变量
  • 使用自旋锁的线程会反复检查锁变量是否可用
  • 自旋锁不会让出CPU,是一种忙等待状态
  • 自旋锁避免了进程或线程上下文切换的开销
  • 操作系统内部很多地方使用的是自旋锁
  • 自旋锁不适合在单核CPU使用

用到的函数和变量

  • pthread_spinlock_t
  • pthread_ spinlock _lock
  • pthread_ spinlock _unlock

代码实现:

#include 
#include 
#include 
//临界资源
int num = 0;
pthread_spinlock_t spinlock;

//PRODUCER
void *producer(void *) {
	int time = 10000000;
	while (time--) {
		pthread_spin_lock(&spinlock);
		num += 1;
		pthread_spin_unlock(&spinlock);
	}
}

//CONSUMER
void *consumer(void *) {

	int time = 10000000;
	while(time--) {		
		pthread_spin_lock(&spinlock);
		num -= 1;
		pthread_spin_unlock(&spinlock);
	}
}

int main() {
	printf("Start int main function.\n");
	pthread_t thread1, thread2;
	pthread_spin_init(&spinlock, 0);//初始化后使用
	pthread_create(&thread1, NULL , &producer, NULL);
	
	pthread_create(&thread2, NULL , &consumer, NULL);
	
	pthread_join(thread1, NULL);
	pthread_join(thread2, NULL);
	printf("The number is %d\n", num);
	return 0;
}

3. 读写锁

应用场景:

  • 临界资源多读少写
  • 读取的时候并不会改变临界资源的值

特点:

  • 读写锁是一种特殊的自旋锁
  • 允许多个读者同时访问资源以提高读性能
  • 对于写操作则是互斥的

涉及到的函数:

  • pthread_rwlock_t
  • pthread_rwlock_rdlock(读锁)
  • pthread_rwlock_wrlock(写锁)

代码实现:

#include 
#include 
#include 
#include 
//临界资源
int num = 0;

//Writer
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
void *writer(void *) {
	int time = 100000;
	while (time --) {
		
			pthread_rwlock_wrlock(&rwlock);//when need to do write operation lock it. forbid other write or read operation.

			num += 1;

			pthread_rwlock_unlock(&rwlock);//unlock it when done.
		
	}
}

//Reader
void *reader(void *) {
	int time = 100000;
	while(time--) {
		if (time % 1000 == 0) {
			pthread_rwlock_rdlock(&rwlock);//when need to do write operation lock it. forbid other write or read operation.
		
			printf("The number is %d\n",num);

			pthread_rwlock_unlock(&rwlock);//unlock it when done.
		}

	}
}

int main() {
	printf("Start int 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("The number is %d\n", num);
	return 0;
}

条件变量 -- 缓冲区

特点:

  • 条件变量是一种相对复杂的线程同步方法
  • 条件变量允许线程睡眠,直到满足某种条件
  • 当满足条件时,可以向该线程信号,通知唤醒

应用场景:

  • 缓冲区小于等于0时,不允许消费者消费,消费者必须等待
  • 缓冲区满时,不允许生产者往缓冲区生产,生产者必须等待

使用到的函数

  • pthread_cond_t
  • pthread_cond_wait(等待条件满足)
  • pthread_cond_signal(等待被唤醒)

代码实现:

#include 
#include 
#include 
#include 
//临界资源
int num = 0;
int MAX_BUF = 100;//缓冲区

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

//PRODUCER
void *producer(void *) {
	int time = 10000000;
	while (time--) {
		pthread_mutex_lock(&mutex);//lock -- mutex
		
		while  (num >= MAX_BUF) {
			pthread_cond_wait(&cond,&mutex); // When run to this, The process change to block status and wait the signal to awake it.
			printf("This BUf is full.\n");
		}
		num += 1; // num <= MAX_BUF , so produce one product.
		printf("The current goods number is :%d\n", num);
		sleep(1);//produce a product cost one second.
		pthread_cond_signal(&cond); // send signal.
		printf("notify the consumer!!\n");

		pthread_mutex_unlock(&mutex);// unlock -- mutex
		sleep(1);
	}
}

//CONSUMER
void *consumer(void *) {

	int time = 10000000;
	while(time--) {
		pthread_mutex_lock(&mutex);
		while (num <= 0) {
			pthread_cond_wait(&cond, &mutex);
			printf("The BUF is empty!!\n");
		}
		num -= 1; // num > 0, so consume one product.
		printf("Consumed one thing!,remain : %d\n", num);
		sleep(1); //  cost one sec to consume.
		pthread_cond_signal(&cond); // send signal to awake the other process.
		printf("notify the producer!!\n");
		pthread_mutex_unlock(&mutex);
	}
}

int main() {
	printf("Start int main function.\n");
	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("The number is %d\n", num);
	return 0;
}

线程同步总结:

线程同步_第1张图片

线程同步_第2张图片

线程同步_第3张图片

你可能感兴趣的:(线程同步)