You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止超过3个线程到达sem_wait()?单向限3辆的道路信号量同步问题求解

现有代码的核心逻辑漏洞

  1. 准入判断无锁保护:while (currActiveDir != direction[0] || numOfCarsOnRoad != 0) 这行判断没有加互斥锁,多线程并发场景下,判断完到执行sem_wait的间隙,全局状态(通行方向、路上车辆数)可能已经被其他线程修改,大量不符合准入条件的线程会提前卡在sem_wait队列里,等sem有信号就直接进入,完全绕过了方向和轮次限制。
  2. 缺少轮次计数:没有限制每一轮同向车的准入数量,只要sem有空位、方向一致,不管是不是同一轮的车都能进入,自然会出现同方向连续通行N辆的情况,只有等所有堵在sem里的同方向车都走完才会切方向。
  3. 全局变量更新无锁:carsGoingW/carsGoingE的增减、currActiveDir的初始化都没有加锁,存在并发读写的脏数据问题。

正确实现思路

核心要保证每一轮的准入判断是原子的,同一轮最多放3辆同向车,放满或者本轮没有更多同方向等待车辆就锁方向,等道路清空后切换方向。调整执行顺序:先拿mutex锁做准入判断,符合条件再执行准入操作,而不是先等sem再拿锁,避免sem队列堆积不符合条件的线程。
具体规则:

  • 所有全局状态的读写都必须在持有mutex锁的场景下进行
  • 车辆线程先拿mutex锁,判断当前是否满足准入条件:
    • 通行方向和自己一致,且当前本轮已经进入的车辆数<3,直接准入,更新路上车辆数和本轮计数
    • 通行方向和自己不一致,或者本轮已经满3辆,释放mutex锁,进入条件变量等待
  • 车辆驶离道路时拿mutex锁更新计数,当路上车辆数为0时,切换通行方向,重置轮次计数,唤醒所有等待的车辆重新竞争准入

修改后的可运行代码

nr.c

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
unsigned int numOfCarsOnRoad = 0;
unsigned int carsGoingW = 0;
unsigned int carsGoingE = 0;
unsigned int currRoundCnt = 0; // 本轮已经进入的车辆数
unsigned long numOfCars = 0;
char currActiveDir;          // W/E
bool currDirInitialized = false;

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

    // 准入等待
    pthread_mutex_lock(&mutex);
    while (currActiveDir != dir_char || currRoundCnt >=3 || numOfCarsOnRoad >=3) {
        pthread_cond_wait(&cond, &mutex);
    }
    // 准入成功
    currRoundCnt++;
    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); // 模拟过桥

    // 驶离更新状态
    pthread_mutex_lock(&mutex);
    if (dir_char == 'W')
        carsGoingW--;
    else
        carsGoingE--;

    numOfCarsOnRoad--;
    printf("Car #%d crossed to the %s! Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad);
    
    // 道路清空,切换方向重置轮次
    if (numOfCarsOnRoad == 0)
    {
        currRoundCnt = 0;
        // 优先切有等待车辆的方向,避免饥饿
        if (currActiveDir == 'W' && carsGoingE > 0)
            currActiveDir = 'E';
        else if (currActiveDir == 'E' && carsGoingW > 0)
            currActiveDir = 'W';
        // 唤醒所有等待线程重新竞争
        pthread_cond_broadcast(&cond);
    }
    pthread_mutex_unlock(&mutex);

    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);
            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));

    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);
        }
    }
    for (i = 0; i < numOfCars; i++)
        pthread_join(cars[i], NULL);
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

nr.h

无需修改,和原代码一致即可。

内容的提问来源于stack exchange,提问作者Stelios Papamichail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 00:45:08