线程间的互斥与同步

660 阅读3分钟

这是我参与更文挑战的第 10 天,活动详情查看: 更文挑战

生产者消费者问题(用于理解线程间互斥与同步的典型事例)


线程间互斥:对于临界资源区,同一时刻只能由一个线程访问。相当于仓库,生产好的产品入库时,就不能从仓库中取东西;从仓库中取东西时,就不能将生产好的产品入库。 线程间同步:有些线程需要有次序的运行,这时候就要用到进程间同步。相当于仓库,只有在仓库中的产品数量大于0时,从仓库中取产品的操作才能进行;只有在仓库中的产品数量小于总数时,新产品入库的操作才能进行。

线程间互斥:互斥锁 线程间同步:信号量

/*************************************************************************
	> File Name: pv.c
	> Author: LiYongjun
	> Mail: 1160606604@qq.com 
	> Created Time: Mon 25 Feb 2019 02:10:47 PM CST
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

pthread_mutex_t mutex;	//互斥锁,用于互斥,保护临界资源,同一时刻只能有一人操作仓库
sem_t empty, full;		//信号量,用于同步,生产者消费者问题
int sum;

void *t1_fun(void *arg)
{
	while(1)
	{
		sem_wait(&full);	//full - 1
		pthread_mutex_lock(&mutex);		//上锁
		sum--;
		printf("thread 1, sum = %d\n", sum);
		sleep(1);
		pthread_mutex_unlock(&mutex);	//解锁
		sem_post(&empty);	//wait + 1
	}
}

void *t2_fun(void *arg)
{
	while(1)
	{
		sem_wait(&empty);	//empty - 1
		pthread_mutex_lock(&mutex);		//上锁
		sum++;
		printf("thread 2, sum = %d\n", sum);
		sleep(3);
		pthread_mutex_unlock(&mutex);	//解锁
		sem_post(&full);	//full + 1
	}
}

int main(int argc, char *argv[])
{
	pthread_mutex_init(&mutex, NULL);	//初始化互斥量
	sem_init(&empty, 0, 5);				//初始化信号量
	sem_init(&full, 0, 0);
	pthread_t t1, t2;
	pthread_create(&t1, NULL, t1_fun, NULL);
	pthread_create(&t2, NULL, t2_fun, NULL);
	while(1)
	{
		sleep(10);
	}
}

执行结果

[root@VM_0_14_centos test]# gcc pv.c -lpthread
[root@VM_0_14_centos test]# ./a.out 
thread 2, sum = 1
thread 2, sum = 2
thread 2, sum = 3
thread 2, sum = 4
thread 2, sum = 5
thread 1, sum = 4
thread 1, sum = 3
thread 1, sum = 2
thread 1, sum = 1
thread 1, sum = 0
thread 2, sum = 1
thread 2, sum = 2
thread 2, sum = 3
thread 2, sum = 4
thread 2, sum = 5

可以看到,仓库中产品的数量永远介于0~5之间(5为仓库最大容量)。

注解: 1.互斥锁 pthread_mutex_init() 函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); pthread_mutex_init()函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。如果参数attr为空,则使用默认的互斥锁属性,默认属性为快速互斥锁 。

pthread_mutex_lock() 当pthread_mutex_lock()返回时,该互斥锁已被锁定。线程调用该函数让互斥锁上锁,如果该互斥锁已被另一个线程锁定和拥有,则调用该线程将阻塞,直到该互斥锁变为可用为止。

pthread_mutex_unlock() pthread_mutex_unlock()可释放mutex引用的互斥锁对象。

2.信号量 sem_init()
原型:int sem_init(sem_t *sem, int pshared, unsigned int value); sem :指向信号量对象 pshared : 指明信号量的类型。不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。 value : 指定信号量值的大小

sem_wait() sem_wait是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。 如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。

sem_post() sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同 时对同一个文件进行读和写操作的两个程序就有可能会引起冲突。