【问题标题】:Thread does not execute 2 different functions 4 times after waiting for signal线程在等待信号后不执行 2 个不同的函数 4 次
【发布时间】:2021-04-03 23:52:00
【问题描述】:

简介

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

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

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

produce_Q 和 producer_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, B=%d, C=%d left\n", stock_A, stock_B, 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", 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 R product\n", w);

    //"Return" resources to stockpile
    stock_A++;
    stock_B++;
    stock_C++;
    printf("Worker[%d] returns stock for R product\n", w);
    printf("Stock: A=%d, B=%d, C=%d left\n", stock_A, stock_B, 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], 0, 1) < 0) error();
    if (sem_init(&sem_worker[1], 0, 1) < 0) error();
    if (sem_init(&sem_worker[2], 0, 1) < 0) error();
    if (sem_init(&sem_worker[3], 0, 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], NULL, 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);
        sem_wait(&sem_worker[w]); //Worker set to wait
        sleep(4); //StackOverflow question here
        printf("Resuming worker's [%d] production for Q\n", w);
    }

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

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

【问题讨论】:

  • sleep() 是一个计时函数,不是同步函数。您可以合理地使用它来模拟您正在模拟的生产机器实际花费的时间,但它在同步它们的活动中没有适当的作用。
  • 您将sem_worker 声明为一个包含两个元素的数组,然后继续使用它,就好像它有四个元素一样。未定义的行为结果。
  • 从多个线程对对象进行非同步、非只读、非原子访问的未定义行为。

标签: c multithreading pthreads semaphore


【解决方案1】:

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

  • 您启动了 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], NULL, produce_R, (void*)i) < 0) error(); //4R production
         }
    

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

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

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

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

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

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

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

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

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

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

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

【讨论】:

  • 非常感谢,感谢所有的努力和深入的回答。这对我有很大帮助。
猜你喜欢
  • 2017-12-21
  • 1970-01-01
  • 2014-12-07
  • 1970-01-01
  • 2018-05-19
  • 1970-01-01
  • 2020-10-30
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多