【问题标题】:Preventing more than 3 threads from reaching sem_wait()防止超过 3 个线程到达 sem_wait()
【发布时间】:2022-01-01 01:41:39
【问题描述】:

我正在尝试解决信号量代表道路的练习的线程同步问题。这条路一次最多可支持 3 辆同向行驶的汽车,应避免挨饿。到目前为止,我下面的代码似乎通过在道路空旷时改变活动方向来避免饥饿。但是,在 numOfCarsOnRoad 更新为非零之前,似乎有超过 3 个汽车/线程可以到达 sem_wait() 行。这意味着(正如您在输出中看到的那样),有时,道路上挤满了 3 辆车,一旦它们开始离开,那些设法到达 sem_wait 的额外车辆,继续进入,当它们完成时,然后是方向更改生效。我很难理解如何防止超过 3 个汽车/线程到达 sem_wait 行,而是等待。

我试图实现的逻辑是最多三辆汽车可以进入信号量(并不总是 3,取决于在状态变量更新之前到达该点的数量),然后任何向另一个方向行驶的汽车都需要等到活动方向改变并且有更多车辆朝同一方向行驶,则需要等到它们的方向在另一轮中再次变为活动状态。

谁能指出我正确的方向或指出我的逻辑有缺陷的地方吗?

nr.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
#include <stdbool.h>
#include "nr.h"

sem_t sem;
pthread_mutex_t mutex;
unsigned int numOfCarsOnRoad = 0;
unsigned int carsGoingW = 0;
unsigned int carsGoingE = 0;
unsigned long numOfCars = 0; // used for thread initializations
char currActiveDir;          // either W or E
bool currDirInitialized = false;

void *crossBridge(void *i)
{
    int id = *((int *)i);
    char direction[5];
    if (rand() % 2 == 0)
    {
        strcpy(direction, "West");
        carsGoingW++;
    }
    else
    {
        strcpy(direction, "East");
        carsGoingE++;
    }
    if (!currDirInitialized)
    {
        currActiveDir = direction[0];
        currDirInitialized = true;
    }

    while (currActiveDir != direction[0] || numOfCarsOnRoad != 0)
        sleep(2);

    sem_wait(&sem); // enter critical region
    printf("Car #%d waiting to pass to the %s...\n", id, direction);
    pthread_mutex_lock(&mutex);
    numOfCarsOnRoad++;
    printf("Car #%d going to the %s. Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad);
    pthread_mutex_unlock(&mutex);

    sleep(1); // cross the road

    if (direction[0] == 'W')
        carsGoingW--;
    else
        carsGoingE--;

    pthread_mutex_lock(&mutex);
    numOfCarsOnRoad--;
    printf("Car #%d crossed to the %s! Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad);
    if (numOfCarsOnRoad == 0) // avoid starvation
    {
        if (currActiveDir == 'W' && carsGoingE > 0)
            currActiveDir = 'E';
        else if (currActiveDir == 'E' && carsGoingW > 0)
            currActiveDir = 'W';
    }
    pthread_mutex_unlock(&mutex);
    sem_post(&sem);

    free(i);
    pthread_exit(NULL);
}

void parseCarArg(int argc, char *argv[])
{
    int i;
    for (i = 0; i < argc; i++)
    {
        if (strcmp(argv[i], "-c") == 0)
        {
            if (++i < argc && strlen(argv[i]) > 0)
                numOfCars = strtol(argv[i], NULL, 10); // convert to long
            if (numOfCars == 0)
            {
                perror("You must enter a number of cars > 0!\n");
                exit(EXIT_FAILURE);
            }
            break;
        }
    }
}

int main(int argc, char *argv[])
{
    if (argc == 0)
        exit(EXIT_FAILURE);
    parseCarArg(argc, argv);
    srand(time(NULL)); // seed the generator using epoch time in millis

    if (sem_init(&sem, 0, 3) == -1)
    {
        perror("Failed to initialize semaphore!\n");
        exit(EXIT_FAILURE);
    }

    if (pthread_mutex_init(&mutex, NULL) != 0)
    {
        perror("Failed to initialize mutex!\n");
        exit(EXIT_FAILURE);
    }

    pthread_t cars[numOfCars];
    int i;
    for (i = 0; i < numOfCars; i++)
    {
        int *id = malloc(sizeof(int));
        *id = i;
        if (pthread_create(&cars[i], NULL, crossBridge, id) != 0)
        {
            perror("Failed to create threads for the cars!\n");
            exit(EXIT_FAILURE);
        }
    }
    // wait for all threads to finish
    for (i = 0; i < numOfCars; i++)
        pthread_join(cars[i], NULL);
    sem_destroy(&sem);
    pthread_mutex_destroy(&mutex);
    return 0;
}

nr.h:

void * crossBridge(void *i);
void parseCarArg(int argc, char *argv[]);

以及以 -c 20 作为输入的示例输出:

Car #0 waiting to pass to the West...
Car #0 going to the West. Number of cars on the road = 1
Car #1 waiting to pass to the West...
Car #1 going to the West. Number of cars on the road = 2
Car #1 crossed to the West! Number of cars on the road = 1
Car #0 crossed to the West! Number of cars on the road = 0
Car #2 waiting to pass to the East...
Car #2 going to the East. Number of cars on the road = 1
Car #2 crossed to the East! Number of cars on the road = 0
Car #18 waiting to pass to the West...
Car #18 going to the West. Number of cars on the road = 1
Car #17 waiting to pass to the West...
Car #17 going to the West. Number of cars on the road = 2
Car #4 waiting to pass to the West...
Car #4 going to the West. Number of cars on the road = 3
Car #4 crossed to the West! Number of cars on the road = 2
Car #9 waiting to pass to the West...
Car #17 crossed to the West! Number of cars on the road = 1
Car #5 waiting to pass to the West...
Car #18 crossed to the West! Number of cars on the road = 0
Car #9 going to the West. Number of cars on the road = 1
Car #5 going to the West. Number of cars on the road = 2
Car #16 waiting to pass to the East...
Car #16 going to the East. Number of cars on the road = 3 <-- example of where the issue occurs
Car #9 crossed to the West! Number of cars on the road = 2
Car #5 crossed to the West! Number of cars on the road = 1
Car #11 waiting to pass to the East...
Car #11 going to the East. Number of cars on the road = 2
Car #8 waiting to pass to the East...
Car #8 going to the East. Number of cars on the road = 3
Car #16 crossed to the East! Number of cars on the road = 2
Car #19 waiting to pass to the East...
Car #19 going to the East. Number of cars on the road = 3
Car #11 crossed to the East! Number of cars on the road = 2
Car #8 crossed to the East! Number of cars on the road = 1
Car #3 waiting to pass to the East...
Car #3 going to the East. Number of cars on the road = 2
Car #6 waiting to pass to the East...
Car #6 going to the East. Number of cars on the road = 3
Car #19 crossed to the East! Number of cars on the road = 2
Car #12 waiting to pass to the East...
Car #12 going to the East. Number of cars on the road = 3
Car #6 crossed to the East! Number of cars on the road = 2
Car #3 crossed to the East! Number of cars on the road = 1
Car #7 waiting to pass to the East...
Car #7 going to the East. Number of cars on the road = 2
Car #12 crossed to the East! Number of cars on the road = 1
Car #7 crossed to the East! Number of cars on the road = 0
Car #15 waiting to pass to the West...
Car #13 waiting to pass to the West...
Car #15 going to the West. Number of cars on the road = 1
Car #14 waiting to pass to the West...
Car #14 going to the West. Number of cars on the road = 2
Car #13 going to the West. Number of cars on the road = 3
Car #13 crossed to the West! Number of cars on the road = 2
Car #14 crossed to the West! Number of cars on the road = 1
Car #15 crossed to the West! Number of cars on the road = 0
Car #10 waiting to pass to the West...
Car #10 going to the West. Number of cars on the road = 1
Car #10 crossed to the West! Number of cars on the road = 0

【问题讨论】:

  • 您的问题标题抓住了解决方案:如果您必须使用信号量来表示网桥的容量,那么在到达信号量之前您需要另一个同步点。我知道您在必须使用的工具中先验受到限制,但是John Bollinger is quite right:计数信号量非常不适合这个问题。我怀疑我们要么遗漏了一些关于允许的解决方案,要么你的老师对如何解决这个问题有一个错误的想法。
  • @pilcrow 感谢您的评论。我只是很难理解如何在信号量之前和两个 while 循环之后同步线程。我再试一次。至于允许的解决方案和方法,遗憾的是我没有遗漏任何内容。这些是他们为此练习设置的约束。他们提到这是为了让我们了解它们的工作原理,但我想正确使用它们并不是他们的首要任务。只是他们希望我们遗憾地看到的功能

标签: c multithreading pthreads semaphore


【解决方案1】:

信号量是这项工作的错误工具。无论您添加什么额外的逻辑来将到达信号量的线程限制在一定数量,都可以很容易地用于完全消除信号量,以支持一个简单的计数器。

您提到条件变量,但未在代码中显示任何变量。 CV + mutex 应该是涉及共享状态的同步问题的通用模式,例如等待在特定方向上行驶的汽车数量,或者自该方向打开以来通过的数量。有时一个信号量就足够了,但您的情况比这更复杂。

您的代码比我愿意分析或重写以获得 SO 答案更复杂,而且我不倾向于为您做作业,但这里有一个解决方案的概要:

  • 有共享变量记录

    • 桥上当前的行进方向
    • 每个方向都有一排等候的汽车
    • 目前桥上的汽车数量(可以通过信号量建模,即使这在一般意义上是不必要的)
    • 在交通停止之前可能进入桥梁的汽车数量,因为有汽车等待从相反方向驶来。
  • 有一个互斥锁用于保护以上所有内容

  • 有一个条件变量,用于与互斥锁相关联,使线程等待直到条件正确才能继续。

  • 当一个线程到达网桥时,它会锁定互斥体并检查它是否可以进入网桥。无论哪种方式,它都会适当地更新状态变量。

  • 如果它不能继续到桥上,线程会将自己排入适当的队列并阻塞条件变量。当它恢复时,它会再次检查它是否可以继续上桥。

  • 当线程退出桥时,它会锁定互斥体,适当地更新状态变量,并广播到 CV。

【讨论】:

  • 您好,让我澄清一下。首先,信号量是练习的必需品,否则我不会使用它。其次,条件变量是指状态变量,我没有 pthread_condition_t 变量。我换个说法吧!最后,我感谢您的 cmets,我会研究它们,但我绝不希望任何人为我“做我的功课”。我正在寻求指导或提示,以防我需要改变我的思维过程。如果您认为上述任何一项为我“做我的功课”,那么千万不要浪费您的时间帮助像我这样的人。不过还是谢谢你。
  • @SteliosPapamichail,虽然这可能与练习的精神背道而驰,但您仍然可以使用信号量来表示道路通行能力,尽管需要额外的同步使得它变得多余。而且您确实需要某种额外的同步来实现既定目标。
猜你喜欢
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-28
  • 1970-01-01
相关资源
最近更新 更多