线程在等待信号后 4 次未执行 2 个不同的函数

如何解决线程在等待信号后 4 次未执行 2 个不同的函数

简介

我打算通过这个程序学习 C 代码中的 ,这是我想出的东西来测试我目前的基础知识。

此程序模拟产品 Q 和 R 的生产线:

Q 需要 1A、1B、1C,R 需要 1A、1C。

produce_Q 和produce_R 函数共享相同的资源

代码

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

sem_t sem_worker[2]; //Semaphores (workers)

//Current stock (initialized as MAX stock)
int stock_A = 5;
int stock_B = 3;
int stock_C = 5;

//Q production method needs 1A,1B and 1C
void *produce_Q(void *worker)
{
    int w = (int) worker; //Worker id
    
    //Wait if ther are not enough tools 
    while (stock_A < 1 || stock_B < 1 || stock_C < 1) {
        printf("Not enough resources for worker[%d] for Q\n",w);
        sem_wait(&sem_worker[w]); //Worker set to wait
        sleep(4); //StackOverflow question here
        printf("Resuming worker's [%d] production for Q\n",w);
    }

    //If there are tool to produce then
    printf("Worker[%d] takes resources from pool for Q\n",w);
    stock_A--;
    stock_B--;
    stock_C--;
    printf("Stock: A=%d,B=%d,C=%d left\n",stock_A,stock_B,stock_C);

    //Random wait to simulate prod
    sleep(rand() % 4);
    printf("Worker[%d] made a Q product\n",w);

    //"Return" resources to stockpile
    stock_A++;
    stock_B++;
    stock_C++;
    printf("Worker[%d] returns stock for P product\n",w);
    printf("Stock: A=%d,stock_C);
}

//R prouction method,needs 1A,1C
void *produce_R(void *worker)
{
    int w = (int) worker; //Worker id
    
    //Wait if ther are not enough tools 
    while (stock_A < 1 || stock_C < 1) {
        printf("Not enough resources for worker[%d] for R\n",w);
        sem_wait(&sem_worker[w]); //Worker set to wait
        sleep(4); //Subject to change for sure
        printf("Resuming worker's [%d] production for R\n",w);
    }

    //If there are tool to produce then
    printf("Worker[%d] takes resources from pool for R\n",stock_C);

    //Random wait to simulate prod
    sleep(rand() % 4);
    printf("Worker[%d] made a R product\n",w);

    //"Return" resources to stockpile
    stock_A++;
    stock_B++;
    stock_C++;
    printf("Worker[%d] returns stock for R product\n",stock_C);
}

int main(void) {

    int i;
    pthread_t th[4]; //Number of threads to work with

    printf("Setting sems...");
    if (sem_init(&sem_worker[0],1) < 0) error();
    if (sem_init(&sem_worker[1],1) < 0) error();
    if (sem_init(&sem_worker[2],1) < 0) error();
    if (sem_init(&sem_worker[3],1) < 0) error();

    //Create threads
    printf("Create threads...");
    for (i = 0; i < 4; i++){
        if (pthread_create(&th[i],NULL,produce_Q,(void*)i) < 0) error(); //4Q production
        if (pthread_create(&th[i],produce_R,(void*)i) < 0) error(); //4R production
    }

    //Wait for thread death
    printf("Waiting for death...");
    for (i = 0; i < 4; i++){
        if (pthread_join(th[i],NULL) < 0) error();
    }

    return 0;

}

问题

我没有得到预期的输出,即每个函数执行 4 次(4Q 和 4R),有时 A、B 或 C 资源将为 0 并且其中一个线程将等待资源可用性。

我在这里控制:

while (stock_A < 1 || stock_B < 1 || stock_C < 1) {
        printf("Not enough resources for worker[%d] for Q\n",w);
    }

但这还不够......我将“睡眠(4)”实现为“有效的补丁”,但这并没有解决我的主要问题:

“有些工人等待资源但不生产之后, 我在等待代码块中遗漏了什么吗?”

解决方法

您的代码有很多问题。其中一些较大的是:

  • 您启动了 8 个线程,但只加入了 4 个(另见下文)。你不能加入其他四个,因为你已经覆盖了他们的线程标识符:

         for (i = 0; i < 4; i++){
             if (pthread_create(&th[i],NULL,produce_Q,(void*)i) < 0) error(); //4Q production
             if (pthread_create(&th[i],produce_R,(void*)i) < 0) error(); //4R production
         }
    

    请特别注意,在每次迭代中,您都将每个新线程的线程标识符存储在同一数组元素中。如果你不加入其他四个,那么你不能相信他们会在整个程序退出时被强行终止之前完成他们的工作。

  • 您正在使用多个独立的信号量来保护对相同共享变量的访问。这是不正确和无效的。多个资源可以被同一个信号量或互斥锁保护,但重点是防止多个线程同时访问共享变量,所以如果线程只需要锁定几个信号量中的任何一个就可以获得访问,那么没有有效的保护。

  • 您必须同步所有对共享变量的访问。这包括在您的 while 循环条件下测试它们的值。

  • 一旦一个线程完成对共享变量的访问,它需要用sem_post()解锁信号量以允许其他线程获取它。您的线程永远不会这样做。在这个模拟中,线程在声明资源之后但在“生产”他们的产品之前解锁信号量是有意义的。在这种情况下,他们会希望在返回资源之前再次锁定它,然后再释放它。

  • 正如我在评论中所写,sleep() 不是同步函数。它没有可同步播放的有效部分。但是,您可能会发现,如果没有这些调用,您的程序将无法取得进展。这是因为需要在满足条件之前暂停线程执行的程序通常应该围绕条件变量构建。这些允许线程挂起直到被通知测试条件是值得的,而不是获取所需的锁并在每一个机会测试条件。

    • 但是,与 pthreads 条件变量一起使用的适当锁定对象是(pthreads)互斥锁,而不是信号量。如果您想使用信号量,那么您需要一个不同的范例。

此外,你的程序有一个有点奇怪的结构。它不仅尝试在互斥量 + 条件变量更自然的情况下使用信号量,而且解决此类生产模拟问题的更典型方法将只涉及每个生产线的一个线程,每个线程执行一个循环以生成多个项目的函数.这种循环结构更适合使用信号量。它会像这样工作:

  1. 每个线程都有一个单独的信号量。 只有一个被初始化为1;另一个(其余的)用值 0 初始化。

  2. 线程被强制进入一个严格的循环序列来获取锁。为此,当每个线程退出临界区时,它会解锁下一个线程的信号量。

  3. 当线程完成其正常工作时,它们必须继续参与循环信号量锁定,直到所有其他线程也完成其工作。

  4. 因此,必须有另外一个或多个共享数据,线程可以通过这些数据确定所有工作何时完成。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-