16.进程同步与死锁——进程同步与信号量

本文讨论了进程如何合作完成任务,重点介绍了信号量在进程同步中的应用,特别是解决生产者-消费者问题。通过案例分析了多个生产者并发时可能出现的问题,并提出了信号量作为解决方案,确保进程的有序执行和资源的有效利用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.进程合作:多进程共同完成一个任务

1.1 司机与售票员

// 司机
while(true)
{
    //等待售票员关门的信号
    启动车辆;
    正常运行;
    到站停车;
}


//售票员
while(true)
{
    关门;
    售票;
    //等待司机到站停车的信号
    开门;
}

1.2 打印机

打印机按照打印队列执行打印,当打印队列中有 6个打印任务,2个进程同时添加打印任务,打印队列的第7个任务是哪个,需要协调

小结
进程有自己的执行条件,有时执行 有时等待,执行完 可能也要 发出信号,让等待该信号的其他进程执行
进程同步 就是 进程有序的执行

1.3 生产者—消费者实例

//共享数据
#define BUFFER_SIZE 10 //缓存空间
typedef struct{...} item;
item buffer[BUFFER_SIZE];
int in = out = counter = 0;

//生产者进程
while(true){
    //当产品 counter == BUFFER_SIZE 就不再生产了,缓存区满,生产者停
    while(counter == BUFFER_SIZE)
        ;
    //产品没达到 BUFFER_SIZE,继续生产
    buffer[in] = item;
    in = (in + 1) % BUFFER_SIZE;
    counter++;//生产者不断生产,发信号让消费者走
}


//消费者进程
while(true){
    //缓冲区空,消费者停
    while(counter == 0)
        ;
    item = buffer[out];
    out = (out + 1) % BUFFER_SIZE;
    counter--;//消费者不断消耗,发信号让生产者走
}

多进程配合,需要等待 阻塞
通过 进程的走走停停 来保证 多进程合作的 合理有序
等待是进程同步的核心

1.3.1 发信号存在的问题

//生产者
while(true){
    //当 counter == BUFFER_SIZE,生产者 sleep,不再生产
    if(counter == BUFFER_SIZE)
        sleep();
    ...
    counter++;
    //当生产者发现 counter == 1,又有产品了,唤醒消费者
    if(counter == 1)
        wakeup(消费者);
}

//消费者
while(true){
    //当消费者发现 counter == 0,进入sleep,不再消费
    if(counter == 0)
        sleep();
    ...
    counter--;
    //当消费者发现 counter == BUFFER_SIZE - 1,就可以生产了,唤醒生产者
    if(counter == BUFFER_SIZE - 1)
        wakeup(生产者);
}

问题:
有多个生产者,P1生产一个item,放入缓冲区,缓冲区满了,P1 sleep
这时,P1 生产一个item,发现缓冲区已经满了,P1 sleep

消费者消耗了一个产品,counter == BUFFER_SIZE - 1,给消费者发信号,唤醒P1,P1 wakeup
消费者再消耗一个产品,counter == BUFFER_SIZE - 2,P2 不能被唤醒了
解决:
单纯依靠counter判断缓冲区的个数是不够的,还要知道有多少进程睡眠了

2.信号量

2.1 引出信号量

根据信号量 判断是否等待,是否执行
信号量 int sem:
0 或者 负数,说明需要等待,让其他进程执行

  • 0 :没有资源了
  • 负数:欠资源了
  • 正数:资源个数

举例:一种资源的数量是8
信号量 = 2:有2个资源可以使用
信号量 = -2,有2个进程在等待资源

上边生产者消费者的例子:
1.缓冲区满,P1执行,P1 sleep,sem = -1,表示一个进程睡眠了
2.P2执行,发现缓冲区满,P2 sleep,sem = -2,表示2个进程睡眠了
3.消费者C执行,wakeup P1,sem = -1
4.消费者C再执行一次,wakeup P2,sem = 0
5.消费者C再执行一次,sem = 1,消费者进程睡眠
6.P3执行,sem = 0,表示没有资源了

2.2 信号量的定义

信号量: 1965年,荷兰学者Dijkstra提出用一种特殊整型变量用来记录,信号用来sleep和wakeup

// semaphore ['seməfɔː] n. 信号
struct semaphore
{
    int value;//资源个数
    PCB *queue;//等待该信号量的进程
}

//消费资源,P来自荷兰语 proberen,即test
P(semaphore s);

//生产资源,V来自荷兰语 verhogen,即increment
V(semaphore s);

P(semaphore s)
{
    s.value--;
    if(s.value < 0){
        sleep(s.queue);
    }
}

V(semaphore s)
{
    s.value++;
    if(s.value <= 0)
    {
        wakeup(s.queue);
    }
}

2.3 用信号量解决生产者-消费者问题

缓存文件 buffer.txt,操作该缓存文件
同时只能有一个进程在写该文件,定义互斥信号量mutex(互斥量)
当有进程在操作缓冲区内容时,P(mutex),造成进程等待
使用完 缓冲区文件,调用V(mutex),释放该文件资源,其他进程可以操作缓冲文件了

//用文件定义 共享缓冲区
int fd = open("buffer.txt");
write(fd, 0, sizeof(int));//写in
write(fd, 0, sizeof(int));//写out

//信号量的定义和初始化
semaphore full = 0;//生产的产品的个数
semaphore empty = BUFFER_SIZE;//空闲缓冲区的个数
semaphore mutex = 1;//互斥信号量

//生产者
Producer(item)
{
    P(empty);//生产者先判断 缓存区个数 empty是否满了,empty == 0,阻塞
    P(mutex);//操作文件前,用mutex防止其他进程操作该文件
    读入in,将item写到in的位置上
    V(mutex);//使用完文件,释放文件资源
    V(full);//生产者生产产品,增加full,消费者就可以消费了
}

//消费者
Consumer()
{
    P(full);//当full == 0,缓冲区空了,阻塞
    P(mutex);
    读入out,从文件中的out位置读出到item,打印item;
    V(mutex);
    V(empty);//消费者消耗产品,增加缓冲区个数,增加empty,生产者就可以继续生产了
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值