经典的进程同步问题

进程同步

1、是什么,主要任务,解决方式,基本概念

是什么: 在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。
主要任务: 对多个相关进程在执行次序上进行协调,使并发执行诸进程之间按照一定的规则(或时序)共享系统资源,并能很好地相互合作,从而使得程序的执行具有可再现性。
解决方式:

  1. 保证多个进程采用互斥的方式访问临界资源
  2. 其次要协调相互合作的各个进程的执行次序

基本概念:

  1. 同步和互斥:
    • 同步:指为了完成某个任务,建立多个进程,这些进程在合作的过程中而产生的相互制约的关系
    • 互斥:指某个进程对一临界资源访问,其它进程等待的一种相互制约关系
  2. 两种制约关系:
    • 间接相互制约关系:由于共享系统资源,而导致这些并发执行的成宿之间形成的制约的关系
    • 直接相互制约关系:由于多个进程之间的合作,而形成的制约关系
  3. 临界资源(临界区):只一次只允许一个进程使用的共享的资源称之为临界资源,把在每个进程访问临界资源的那段代码称之为临界区

2、两种机制

信号量机制:

  • 整型信号量
  • 记录型信号量
  • 信号量集

管程机制: 管程是由局部于自己的若干公共变量及其说明和所有访问这些公共变量的过程所组成的软件模块。

3、经典问题

3.1、生产者——消费者问题

3.1.1、问题描述

系统中有两个进程,一个进程用于生产资源,一个进程用于消费资源。这两个进程共享一块大小确定的存放资源的区域(缓冲池)。

  • 当区域内的资源没有装满时,生产进程可以往里面放资源;当区域内的资源满时,生产进程需等待
  • 当区域内的资源不为空时,消费进程可以从里面取出资源;当区域内的资源为空时,消费进程需等待
3.1.2、设计

1、因为两个进程之间的访问,不能同时进行,即生产进程放入资源时,消费进程不能取资源;消费进程取资源时,生产进程不能放入资源, 所以需要一个互斥信号量:mutex,初始值为1,表示进程能直接执行
2、生产进程需要判断缓冲池是不是满的,所以需要一个空缓冲区,empty,初始值为n,当空缓冲区为0时表示已经满了
3、消费进程需要判断缓冲池是不是空的,所以需要一个满缓冲区,full,初始值为0,当满缓冲区为0时,表示缓冲池中没数据

3.1.3、实现
semaphore mutex=1,empty=n,full=0;
void producer(){
	do{
		wait(empty);		//如果empty<=0,等待;否则empty--
		wait(mutex);		//如果mutex<=0,等待;否则mutex--
		//生产
		signel(mutex);		//mutex++
		signel(full);		//full++
	}while(TRUE);
}

void consumer(){
	do{
		wait(full);			//如果full<=0,等待;否则full--
		wait(mutex);		//如果mutex<=0,等待;否则mutex--
		//生产
		signel(mutex);		//mutex++
		signel(empty);		//empty++
	}while(TRUE);
}

void main(){
	cobegin
		producer();consumer();
	coend
}

3.2、哲学家进餐

3.2.1、问题描述

一张圆桌上坐着五名哲学家,每两名哲学家之间摆一根筷子,一共五根筷子,一个哲学家只有左手右手都拿到一只筷子才能进餐。进餐完毕,筷子放回原处。

3.2.2、设计

因为有五只筷子,所以筷子为临界资源,即chopstick[5]={1,1,1,1,1};如代码1。
问题: 假如五个哲学家,同时去拿左边的筷子时,五个信号量将都会变为0,将永远拿不到右边的筷子,产生死锁
改进:

  1. 至多运行有四位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能进餐,可以增加一个信号量mutex,初始值为4
  2. 仅当哲学家左右两只筷子都能用时,才允许他那契筷子进餐
  3. 奇数哲学家先拿左边的,偶数哲学家先拿右边的
3.2.3、实现

代码1(有可能引起死锁):

semaphore chopstick[5]={1,1,1,1,1};
void get(){
	do{
		wait(chopstick[i]);		
		wait(chopstick[(i+1)%5]);		
		//eat
		signel(chopstick[i]);		
		signel(chopstick[(i+1)%5]);		
	}while(TRUE);
}

代码2:

semaphore chopstick[5]={1,1,1,1,1};
void get(){
	do{
		wait(mutex);
		wait(chopstick[i]);		
		wait(chopstick[(i+1)%5]);		
		//eat
		signel(chopstick[i]);		
		signel(chopstick[(i+1)%5]);	
		signel(mutex);	
	}while(TRUE);
}

3.3、读者——写者

3.3.1、问题描述

一个文件可以被读和被写,多个进程操作这一个共享对象

  • 因为读操作不会引起文件混乱,所以可以同时有多个进程读
  • 写操作会引起文件混乱,所以写操作不允许和其他读操作或者写操作同时访问对象
3.3.2、设计
  1. 因为可以有多个进程读,所以写入的时候,需要判断当前是否还有进程在读,如果有,则不能写,等待,如果没有,则可以写入,所以需要一个读缓冲池,readcount,初始值为0
  2. 因为readcount是可以被一个或多个进程访问的临界资源,所以针对以readcount应该设置一个互斥信号量rmutex,初始值为1
  3. 因为写入的时候,不允许其他操作,因此需要一个写的互斥信号量wmutex,初始值为1
3.3.3、实现
semaphore rmutex=1,wmutex2=1;
int readcount=0;
void reader(){
	do{
		wait(rmutex);
		if(readcount==0)wait(wmutex);
		readcount++;
		signal(rmutex);
		//读
		wait(rmutex);
		readcount--;
		if(readcount==0)signal(wmutex);
		signal(rmutex);
	}while(TRUE);
}

void writer(){
	do{
		wait(wmutex);
		//写
		signal(wmutex);
	}while(TRUE);
}
void main(){
	cobegin
		reader();writer();
	coend
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经典进程同步问题通常包括生产者-消费者问题、读者-问题和哲学家就餐问题等。这里以生产者-消费者问题为例,简单介绍一下如何在 C 语言中实现进程同步。 生产者-消费者问题是指多个进程共享同一个缓冲区,其中生产者进程向缓冲区中入数据,而消费者进程则从缓冲区中读取数据。为了避免竞争条件和死锁等问题,需要使用同步机制来保证进程之间的协调工作。 使用信号量可以实现进程同步。信号量是一个计数器,用于控制对共享资源的访问。在 C 语言中,我们可以使用 POSIX 信号量库来创建和使用信号量。 下面是一个简单的生产者-消费者问题代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int in = 0, out = 0; sem_t empty; // 缓冲区空闲信号量 sem_t full; // 缓冲区满信号量 pthread_mutex_t mutex; // 互斥锁 void *producer(void *arg) { int item; while (1) { item = rand() % 100; // 生产一个随机数 sem_wait(&empty); // 当缓冲区空间为 0 时等待 pthread_mutex_lock(&mutex); buffer[in] = item; in = (in + 1) % BUFFER_SIZE; // 循环缓冲区 printf("Producer produced item %d\n", item); pthread_mutex_unlock(&mutex); sem_post(&full); // 增加缓冲区中的项目数 } pthread_exit(NULL); } void *consumer(void *arg) { int item; while (1) { sem_wait(&full); // 当缓冲区中没有项目时等待 pthread_mutex_lock(&mutex); item = buffer[out]; out = (out + 1) % BUFFER_SIZE; // 循环缓冲区 printf("Consumer consumed item %d\n", item); pthread_mutex_unlock(&mutex); sem_post(&empty); // 增加缓冲区的空闲空间 } pthread_exit(NULL); } int main(int argc, char **argv) { pthread_t producer_thread, consumer_thread; sem_init(&empty, 0, BUFFER_SIZE); // 初始化信号量 sem_init(&full, 0, 0); pthread_mutex_init(&mutex, NULL); pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); sem_destroy(&empty); // 销毁信号量和互斥锁 sem_destroy(&full); pthread_mutex_destroy(&mutex); return 0; } ``` 在这个示例中,我们使用了两个信号量 `empty` 和 `full` 来控制缓冲区的空闲空间和项目数。当 `empty` 为 0 时,表示缓冲区已满,生产者需要等待;当 `full` 为 0 时,表示缓冲区没有项目,消费者需要等待。使用互斥锁 `mutex` 来保护共享资源的访问,避免竞争条件。 当生产者生成一个随机数时,将其入缓冲区,同时增加 `in` 指针,表示缓冲区中的项目数增加了一个。当消费者读取缓冲区中的项目时,将其存储到 `item` 中,并将 `out` 指针增加,表示缓冲区中的项目数减少了一个。 上述代码中,生产者和消费者都是无限循环运行的,可以使用 `pthread_cancel()` 函数来终止它们的运行。此外,还需要注意的是,在使用信号量等同步机制时,不要忘记释放已经申请的资源,否则可能会导致死锁等问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值