操作系统——吸烟者问题

吸烟者问题

问题描述

​ 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)

角色与行为

供应者

  • 无限循环地提供香烟和火柴的组合
  • 等待吸烟者完成吸烟的通知
  • 同步机制确保每次只为一个吸烟者提供材料

吸烟者

  • 请求香烟和火柴
  • 吸烟
  • 吸烟完成后通知供应者

同步与互斥

信号量分析

  • 使用信号量实现同步

    • 组合一:提供纸和胶水 offer1
    • 组合二:提供烟草和胶水 offer2
    • 组合三:提供烟草和纸 offer3
  • 互斥访问供应区

    • 桌子信号量 desk
    • 互斥 mutex

semaphore offer1 = 0;

semaphore offer2 = 0;

semaphore offer3 = 0;

semaphore desk = 1;

semaphoer mutex = 1;

伪代码

// 定义信号量
semaphore offer1 = 0;  // 用于表示物品1是否被提供
semaphore offer2 = 0;  // 用于表示物品2是否被提供
semaphore offer3 = 0;  // 用于表示物品3是否被提供
semaphore desk = 1;    // 用于表示桌子是否可用
semaphore mutex = 1;   // 用于保证进程 p0 的互斥访问

// 进程 p0:提供物品
process p0() {
    i++;            // 增加计数器 i
    i = i % 3;      // i 的值在 0, 1, 2 循环
    p(desk);        // 申请桌子资源
    p(mutex);       // 进入临界区,保证互斥访问
    if (i == 1)     // 根据 i 的值提供不同的物品
        v(offer1);  // 提供组合1
    else if (i == 2)
        v(offer2);  // 提供组合2
    else
        v(offer3);  // 提供组合3
    v(mutex);       // 退出临界区
}

// 进程 p1:烟民1
process p1() {
    p(offer1);      // 等待物品1
    p(mutex);       // 进入临界区,保证互斥访问
    吸烟;           
    v(mutex);       // 退出临界区
    v(desk);        // 释放桌子
}

// 进程 p2:烟民2
process p2() {
    p(offer2);      // 等待物品2
    p(mutex);       // 进入临界区,保证互斥访问
    吸烟;           
    v(mutex);       // 退出临界区
    v(desk);        // 释放桌子
}

// 进程 p3:烟民3
process p3() {
    p(offer3);      // 等待物品3
    p(mutex);       // 进入临界区,保证互斥访问
    吸烟;           
    v(mutex);       // 退出临界区
    v(desk);        // 释放桌子
}

思考:是否可以继续优化信号量?
经过分析发现mutex可以去掉,因为desk已经完成了互斥访问。优化后伪代码如下:

semaphore offer1 = 0;
semaphore offer2 = 0;
semaphore offer3 = 0;
semaphore desk = 1;

process p0() {
    i++;
    i = i % 3;
    p(desk);
    if (i == 1)
        v(offer1);
    else if (i == 2)
        v(offer2);
    else
        v(offer3);
}

process p1() {
    p(offer1);
    吸烟;
    v(desk);
}

process p2() {
    p(offer1);
    吸烟;
    v(desk);
}

process p3() {
    p(offer1);
    吸烟;
    v(desk);
}

c语言程序验证结果:

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

// 初始化信号量
sem_t offer1;  // 表示桌上有一种组合材料
sem_t offer2;  // 表示桌上有另一种组合材料
sem_t offer3;  // 表示桌上有第三种组合材料
sem_t desk;    // 表示桌子是否空闲
int i = 0;

// 代理人线程函数
void* agent(void* arg) {
    while (1) {
    	i++;
    	i = i % 3;
        sem_wait(&desk);  //P(desk); 
        
        if (i == 0) {
        	printf("\n");
            printf("代理人提供烟草和纸\n");
            sem_post(&offer1); //V(offer1); 
        } else if (i == 1) {
        	printf("\n");
            printf("代理人提供纸和火柴\n");
            sem_post(&offer2); //V(offer2); 
        } else {
        	printf("\n");
            printf("代理人提供火柴和烟草\n");
            sem_post(&offer3); //V(offer3); 
        }
        sleep(1);  // 模拟代理人放置材料的时间
    }
    return NULL;
}

// 吸烟者线程函数(拥有烟草的吸烟者)
void* smoker_with_tobacco(void* arg) {
    while (1) {
        sem_wait(&offer2);  // 等待纸和火柴
        printf("拥有烟草的吸烟者正在吸烟\n");
        sleep(0.5);  // 模拟吸烟时间
        sem_post(&desk);
    }
    return NULL;
}

// 吸烟者线程函数(拥有纸的吸烟者)
void* smoker_with_paper(void* arg) {
    while (1) {
        sem_wait(&offer3);  // 等待火柴和烟草
        printf("拥有纸的吸烟者正在吸烟\n");
        sleep(0.5);  // 模拟吸烟时间
        sem_post(&desk);
    }
    return NULL;
}

// 吸烟者线程函数(拥有火柴的吸烟者)
void* smoker_with_match(void* arg) {
    while (1) {
        sem_wait(&offer1);  // 等待烟草和纸
        printf("拥有火柴的吸烟者正在吸烟\n");
        sleep(0.5);  // 模拟吸烟时间
        sem_post(&desk);
    }
    return NULL;
}

int main() {
    // 初始化信号量
    sem_init(&offer1, 0, 0);
    sem_init(&offer2, 0, 0);
    sem_init(&offer3, 0, 0);
    sem_init(&desk, 0, 1);

    // 创建线程
    pthread_t agent_thread;
    pthread_t smoker_tobacco_thread;
    pthread_t smoker_paper_thread;
    pthread_t smoker_match_thread;

    pthread_create(&agent_thread, NULL, agent, NULL);
    pthread_create(&smoker_tobacco_thread, NULL, smoker_with_tobacco, NULL);
    pthread_create(&smoker_paper_thread, NULL, smoker_with_paper, NULL);
    pthread_create(&smoker_match_thread, NULL, smoker_with_match, NULL);

    // 等待线程结束(实际中这些线程是无限循环的,不会自然结束)
    pthread_join(agent_thread, NULL);
    pthread_join(smoker_tobacco_thread, NULL);
    pthread_join(smoker_paper_thread, NULL);
    pthread_join(smoker_match_thread, NULL);

    // 销毁信号量
    sem_destroy(&offer1);
    sem_destroy(&offer2);
    sem_destroy(&offer3);
    sem_destroy(&desk);

    return 0;
}

##运行结果分析:正确请添加图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
吸烟者问题是一个经典的同步问题,它涉及到三个进程:一个代表桌子,另外两个代表吸烟者。 桌子进程负责往桌子上放烟草、纸和火柴。每个吸烟者进程需要烟草、纸和火柴中的两个才能抽烟。当一个吸烟者进程拥有了两个必需的材料之后,它将抽烟并通知桌子进程继续放置烟草、纸和火柴。 以下是一种可能的实现: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t match_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t paper_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t tobacco_cond = PTHREAD_COND_INITIALIZER; int match_on_table = 0; int paper_on_table = 0; int tobacco_on_table = 0; void *agent(void *arg) { while (1) { pthread_mutex_lock(&table_lock); while (match_on_table && paper_on_table && tobacco_on_table) { pthread_cond_wait(&match_cond, &table_lock); pthread_cond_wait(&paper_cond, &table_lock); pthread_cond_wait(&tobacco_cond, &table_lock); } int r = rand() % 3; if (r == 0) { match_on_table = 1; printf("agent: put match on table\n"); pthread_cond_signal(&paper_cond); pthread_cond_signal(&tobacco_cond); } else if (r == 1) { paper_on_table = 1; printf("agent: put paper on table\n"); pthread_cond_signal(&match_cond); pthread_cond_signal(&tobacco_cond); } else { tobacco_on_table = 1; printf("agent: put tobacco on table\n"); pthread_cond_signal(&match_cond); pthread_cond_signal(&paper_cond); } pthread_mutex_unlock(&table_lock); sleep(1); } return NULL; } void *smoker_match(void *arg) { while (1) { pthread_mutex_lock(&table_lock); while (!paper_on_table || !tobacco_on_table) { pthread_cond_wait(&match_cond, &table_lock); } printf("smoker with match: start smoking\n"); match_on_table = 0; paper_on_table = 0; tobacco_on_table = 0; pthread_mutex_unlock(&table_lock); sleep(1); } return NULL; } void *smoker_paper(void *arg) { while (1) { pthread_mutex_lock(&table_lock); while (!match_on_table || !tobacco_on_table) { pthread_cond_wait(&paper_cond, &table_lock); } printf("smoker with paper: start smoking\n"); match_on_table = 0; paper_on_table = 0; tobacco_on_table = 0; pthread_mutex_unlock(&table_lock); sleep(1); } return NULL; } void *smoker_tobacco(void *arg) { while (1) { pthread_mutex_lock(&table_lock); while (!match_on_table || !paper_on_table) { pthread_cond_wait(&tobacco_cond, &table_lock); } printf("smoker with tobacco: start smoking\n"); match_on_table = 0; paper_on_table = 0; tobacco_on_table = 0; pthread_mutex_unlock(&table_lock); sleep(1); } return NULL; } int main() { pthread_t agent_thread; pthread_t smoker_match_thread; pthread_t smoker_paper_thread; pthread_t smoker_tobacco_thread; pthread_create(&agent_thread, NULL, agent, NULL); pthread_create(&smoker_match_thread, NULL, smoker_match, NULL); pthread_create(&smoker_paper_thread, NULL, smoker_paper, NULL); pthread_create(&smoker_tobacco_thread, NULL, smoker_tobacco, NULL); pthread_join(agent_thread, NULL); pthread_join(smoker_match_thread, NULL); pthread_join(smoker_paper_thread, NULL); pthread_join(smoker_tobacco_thread, NULL); return 0; } ``` 在这个实现中,桌子进程负责放置材料,每个吸烟者进程会不断地等待桌子进程放置必需的材料。当桌子进程放置好两个必需的材料之后,对应的吸烟者进程会抽烟,并将桌子清空,通知桌子进程继续放置材料。这个实现使用了 pthread 库来实现线程同步

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值