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

C语言多线程窄桥通行实现中numOfCarsOnRoad计数不一致问题咨询

问题根因与修复方案

问题1:numOfCarsOnRoad计数异常原因

  • 线程创建传参错误:创建线程时传入了循环变量i的地址,所有线程共享同一块内存空间,循环执行过程中i的值持续变化,最终大部分线程拿到的id都是错误值,直接导致输出混乱。
  • 共享变量无同步保护:numOfCarsOnRoad、currActiveDir、carsGoingW、carsGoingE都是多线程共享的全局变量,你在临界区(sem_wait保护区域)外直接读写这些变量,会产生竞态条件,读到的数值均为脏数据。
  • 等待判断逻辑位置错误:你把道路状态的判断逻辑放在了信号量获取之前,多个线程可能同时通过判断条件后等待信号量,等拿到信号量时道路状态已经发生变化,最终计数不符合预期。

问题2:sleep(1)后线程执行顺序和启动顺序不一致原因

sleep(1)的作用是让线程至少阻塞1秒,而非1秒后立刻获得CPU执行权。操作系统的线程调度是抢占式的,计时结束的线程需要等待调度器分配CPU时间片,执行顺序不存在必然的先后关系。如果需要保证同方向车辆的通行顺序,可以额外实现FIFO等待队列,按申请顺序放行车辆即可,不需要强制依赖启动顺序。


修复代码

头文件 ask2.h

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

主实现代码

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

sem_t sem;
int numOfCarsOnRoad = 0;
int carsGoingW = 0;
int carsGoingE = 0;
long numOfCars = 0;       // used for thread initializations
char currActiveDir = '_'; // either W or E ( _ initially)
pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER; // 保护rand调用

void *crossBridge(void *arg)
{
    int id = (int)arg; // 直接拿传过来的值,不再解引用地址
    char direction[5];
    // 线程安全的rand调用
    pthread_mutex_lock(&rand_mutex);
    if (rand() % 2 == 0) {
        strcpy(direction, "West");
    }
    else {
        strcpy(direction, "East");
    }
    pthread_mutex_unlock(&rand_mutex);

    printf("Car #%d waiting to pass to the %s...\n", id, direction);
    sem_wait(&sem); // 先拿信号量再操作共享变量
    // 临界区内判断状态,不符合就释放信号量等待重试
    while (currActiveDir != '_' && currActiveDir != direction[0] || numOfCarsOnRoad == 3) {
        sem_post(&sem);
        sleep(1);
        sem_wait(&sem);
    }
    // 更新方向和等待计数
    if (currActiveDir == '_') {
        currActiveDir = direction[0];
    }
    if (direction[0] == 'W') carsGoingW++;
    else carsGoingE++;

    numOfCarsOnRoad++;
    printf("Car #%d going to the %s. Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad);
    sem_post(&sem); // 操作完共享变量就释放信号量,不要把sleep放在临界区里

    sleep(1); // 模拟通行时间,不需要放在临界区

    sem_wait(&sem); // 再次拿信号量修改状态
    numOfCarsOnRoad--;
    printf("Car #%d crossed to the %s! Number of cars on the road = %d\n", id, direction, numOfCarsOnRoad);
    if(direction[0] == 'W') carsGoingW--;
    else carsGoingE--;

    // 避免饥饿逻辑
    if (numOfCarsOnRoad == 0) {
        if (currActiveDir == 'W' && carsGoingE > 0)
            currActiveDir = 'E';
        else if (currActiveDir == 'E' && carsGoingW > 0)
            currActiveDir = 'W';
        else
            currActiveDir = '_';
    }
    sem_post(&sem);

    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 < 2)
        exit(EXIT_FAILURE);
    parseCarArg(argc, argv);
    srand(time(NULL)); // seed the generator using epoch time

    if (sem_init(&sem, 0, 1) == -1) // 信号量改作互斥锁用,保护所有共享变量,最大通行3辆的逻辑用numOfCarsOnRoad判断
    {
        perror("Failed to initialize semaphore!\n");
        exit(EXIT_FAILURE);
    }

    pthread_t cars[numOfCars];
    int i;
    for (i = 0; i < numOfCars; i++)
    {
        // 直接传i的值,不要传地址
        if (pthread_create(&cars[i], NULL, crossBridge, (void*)i) != 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(&rand_mutex);
    return 0;
}

内容的提问来源于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 23:45:05