繁体   English   中英

防止超过 3 个线程到达 sem_wait()

[英]Preventing more than 3 threads from reaching sem_wait()

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

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

有人可以指出我正确的方向或指出我的逻辑有缺陷的地方吗?

编号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 作为输入的示例 output:

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

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

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

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

  • 有共享变量记录

    • 桥上当前的行进方向
    • 每个方向准备的汽车数量
    • 目前桥上的汽车数量
    • 由于等待相反方向的汽车,在交通可以停止之前可能进入桥梁的汽车数量。
  • 有一个互斥锁用于保护以上所有内容

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

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

  • 如果它不能继续到桥上,一个线程会阻塞条件变量,当它继续时,它会再次检查它是否可以继续。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM