线程学习(生产者消费者问题&哲学家吃饭问题)

线程的概念:

概念:

进程:从操作系统核心角度来说,进程是操作系统调度和分配内存资源,CPU时间片的基本单位,它为正在运行应用程序提供了运行环境。简单来说,进程是应用程序的一次运行活动。

线程:线程是程序内部的一个顺序代码流,它是CPU调度资源的最基本单位。

关系:

(1) 线程并不能独立地执行,它必须依附在一个运行的应用程序上(即进程上),进程至少包含一个线程且可以包含多个线程,而线程只能依附于一个进程上;

(2) 进程是操作系统资源管理的实体,而线程是进程的实体。

区别:

当操作系统分配给进程资源后,同属于一个进程的多个线程之间可以相互共享进程中的内存资源,原因是线程没有自己独立的内存资源,它只有自己的执行堆栈和局部变量。而多个进程之间每个进程都拥有自己的一整套变量,即每个进程都拥有独立的内存单元。这就使得多线程之间的通信比多进程之间的通信更加容易和高效。

生产者消费者问题:

问题描述:

生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

问题分析:

1.在缓冲区为空时,消费者不能再进行消费
2.在缓冲区为满时,生产者不能再进行生产
3.在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作,即保持线程间的同步
4.注意条件变量与互斥锁的顺序

代码展示:

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

#define N 2//消费者或生产者的数目
#define M 10//缓冲数目

int in=0;//生产者放置产品的位置
int out=0;//消费者读取产品的位置
int buff[M]={
    
    0};//缓冲初始化为0,开始时并没有产品
sem_t empty_sem;//同步信号量,当满了时阻止生产者放产品
sem_t full_sem;//同步信号量,当没产品时阻止消费者消费
pthread_mutex_t mutex;//互斥信号量,保证一次只有一个线程访问缓冲
int product_id=0;//生产者id
int consume_id=0;//消费者id
//打印缓冲内容
void print()
{
    
    
	int i;
	for(i=0;i<M;i++){
    
    
		printf("%d",buff[i]);
		printf("\n");
	}
}
//生产者方法
void *product()
{
    
    
	int id=++product_id;

	while(1){
    
    
		sleep(1);

		sem_wait(&empty_sem);
		pthread_mutex_lock(&mutex);

		in=in%M;
		printf("product %d in %d.like:\n",id,in);

		buff[in]=1;
		print();
		in++;

		pthread_mutex_unlock(&mutex);
		sem_post(&full_sem);
	}
}
//消费者方法
void *consume()
{
    
    
	int id=++consume_id;
	while(1){
    
    
		sleep(1);

		sem_wait(&full_sem);
		pthread_mutex_lock(&mutex);

		out=out%M;
		printf("consume %d in %d.like:\n",id,out);

		buff[out]=0;
		print();
		out++;

		pthread_mutex_unlock(&mutex);
		sem_post(&empty_sem);
	}
}

int main(int argc,char **argv)
{
    
    
	pthread_t id1[N];
	pthread_t id2[N];
	int i;
	//初始化同步信号量,及初始化互斥信号量
	int ini1=sem_init(&empty_sem,0,M);
	int ini2=sem_init(&full_sem,0,0);
	int ini3=pthread_mutex_init(&mutex,NULL);
	//创建生产者线程
	for(i=0;i<N;i++){
    
    
		pthread_create(&id1[i],NULL,product,(void*)(&i));
	}
	//创建消费者线程
	for(i=0;i<N;i++){
    
    
		pthread_create(&id2[i],NULL,consume,NULL);
	}
	//挂起线程
	for(i=0;i<N;i++){
    
    
		pthread_join(id1[i],NULL);
		pthread_join(id2[i],NULL);
	}
	//销毁信号量
	sem_destroy(&empty_sem);
	sem_destroy(&full_sem);
	//销毁互斥锁
	pthread_mutex_destroy(&mutex);
}

哲学家吃饭问题:

问题描述:

有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。

问题分析:

1.只有拿到两只筷子时,哲学家才能吃饭。
2.如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
3.任一哲学家在自己未拿到两只筷子吃饭前,不会放下手中拿到的筷子。

问题解法一:

通过互斥锁 mutex 对哲学家进餐之前取左侧和右侧筷子的操作进行保护,可以防止死锁的出现。(当第i个哲学家将左右筷子都拿到了才允许其他哲学家拿筷子)

代码展示:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>

#define N 5

sem_t chopsticks[N];//设置5种信号量

pthread_mutex_t mutex;//定义互斥锁

int philosophers[N]={
    
    0,1,2,3,4};//代表5个哲学家的编号

void delay(int len){
    
    //时间缓冲函数
	int i=rand()%len;
	int x;
	while(i>0){
    
    
		x=rand()%len;
		while(x>0){
    
    
			x--;
		}
		i--;
	}
}

void *philosopher(void *arg){
    
    
	int i=*(int *)arg;
	int left=i;//左筷子的编号与哲学家的编号一致
	int right=(i+1)%N;//右筷子的编号为哲学家编号+1
	while(1){
    
    
		printf("哲学家%d正在思考问题\n",i);
		delay(60000);

		printf("哲学家%d饿了\n",i);

		pthread_mutex_lock(&mutex);//加锁

		sem_wait(&chopsticks[left]);//此时这个哲学家左筷子的信号量-1后>=时,表示能继续执行
		printf("哲学家%d拿起了%d号筷子,现在只有一只筷子,不能进餐\n",i,left);
		sem_wait(&chopsticks[right]);
		printf("哲学家%d拿起了%d号筷子\n",i,right);

		pthread_mutex_unlock(&mutex);//解锁

		printf("哲学家%d现在有两只筷子,开始进餐\n",i);
		delay(6000);
		sem_post(&chopsticks[left]);
		printf("哲学家%d放下了号%d筷子\n",i,left);
		sem_post(&chopsticks[right]);
		printf("哲学家%d放下了号%d筷子\n",i,right);
	}
}

int main(int argc,char **argv){
    
    
	int i;
	srand(time(NULL));
	pthread_t philo[N];
	//信号量初始化
	for(i=0;i<N;i++){
    
    
		sem_init(&chopsticks[i],0,1);
	}

	pthread_mutex_init(&mutex,NULL);//互斥锁初始化
	//创建线程
	for(i=0;i<N;i++){
    
    
		pthread_create(&philo[i],NULL,philosopher,&philosophers[i]);
	}
	//挂起线程
	for(i=0;i<N;i++){
    
    
		pthread_join(philo[i],NULL);
	}
	//销毁信号量
	for(i=0;i<N;i++){
    
    
		sem_destroy(&chopsticks[i]);
	}

	pthread_mutex_destroy(&mutex);//销毁互斥锁
}

问题解法二:

至多只允许四位哲学家同时去拿左筷子,最终能保证至少有一位哲学家能进餐,并在用完后释放两只筷子供他人使用。

代码展示:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>

#define N 5

sem_t chopsticks[N];//设置5种信号量
sem_t m;//最多允许有m(4)个哲学家同时拿起左筷子

int philosophers[N]={
    
    0,1,2,3,4};//代表哲学家的5个编号

void delay(int len){
    
    //时间缓冲函数
	int i=rand()%len;
	int x;
	while(i>0){
    
    
		x=rand()%len;
		while(x>0){
    
    
			x--;
		}
		i--;
	}
}

void *philosopher(void *arg){
    
    
	int i=*(int *)arg;
	int left=i;//左筷子的编号与哲学家编号一致
	int right=(i+1)%N;//右筷子的编号为哲学家编号+1
	while(1){
    
    
		printf("哲学家%d正在思考问题\n",i);
		delay(60000);

		printf("哲学家%d饿了\n",i);
		sem_wait(&m);//如果前4个哲学家同时拿起左筷子,则第五个不能拿起左筷子,保证至少有一个哲学家能吃到饭
		sem_wait(&chopsticks[left]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行
		printf("哲学家%d拿起了%d号筷子,现在只有一直筷子,不能进餐\n",i,left);
		sem_wait(&chopsticks[right]);
		printf("哲学家%d拿起了%d号筷子,现在有两只筷子,开始进餐\n",i,right);
		delay(60000);
		sem_post(&chopsticks[left]);
		printf("哲学家%d放下了%d号筷子\n",i,left);
		sem_post(&m);//当哲学家放下左筷子时,信号量m+1
		sem_post(&chopsticks[right]);
		printf("哲学家%d放下了%d号筷子\n",i,right);
	}
}

int main (int argc,char **argv){
    
    
	int i;
	srand(time(NULL));
	pthread_t philo[N];
	//信号量初始化
	for(i=0;i<N;i++){
    
    
		sem_init(&chopsticks[i],0,1);
	}
	sem_init(&m,0,4);
	//创建线程
	for(i=0;i<N;i++){
    
    
		pthread_create(&philo[i],NULL,philosopher,&philosophers[i]);
	}
	//挂起线程
	for(i=0;i<N;i++){
    
    
		pthread_join(philo[i],NULL);
	}
	//销毁信号量
	for(i=0;i<N;i++){
    
    
		sem_destroy(&chopsticks[i]);
	}
	sem_destroy(&m);
}

猜你喜欢

转载自blog.csdn.net/ABded/article/details/106589893