进程:从操作系统核心角度来说,进程是操作系统调度和分配内存资源,CPU时间片的基本单位,它为正在运行应用程序提供了运行环境。简单来说,进程是应用程序的一次运行活动。
线程:线程是程序内部的一个顺序代码流,它是CPU调度资源的最基本单位。
(1) 线程并不能独立地执行,它必须依附在一个运行的应用程序上(即进程上),进程至少包含一个线程且可以包含多个线程,而线程只能依附于一个进程上;
(2) 进程是操作系统资源管理的实体,而线程是进程的实体。
当操作系统分配给进程资源后,同属于一个进程的多个线程之间可以相互共享进程中的内存资源,原因是线程没有自己独立的内存资源,它只有自己的执行堆栈和局部变量。而多个进程之间每个进程都拥有自己的一整套变量,即每个进程都拥有独立的内存单元。这就使得多线程之间的通信比多进程之间的通信更加容易和高效。
生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
1.在缓冲区为空时,消费者不能再进行消费
2.在缓冲区为满时,生产者不能再进行生产
3.在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作,即保持线程间的同步
4.注意条件变量与互斥锁的顺序
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#include
#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);
}