pthread同步建模为火车站

时间:2015-06-26 02:17:31

标签: c multithreading pthreads

我正在用pthreads为火车站建模。每列火车都有自己的线程和自己的条件变量,用于监控其对主轨道的访问。列车信息从文件中读取,格式为:

(方向):(加载时间):(穿越时间)

一次只能有一列火车在主轨道上。火车不能在主轨道上,除非它们已经装好并准备就绪。

有一个单独的调度员线程负责协调对所有列车之间的主轨道的访问。调度员线程还根据各种规则(例如列车进入的方向)决定哪列火车可以进入。如果我能按照他们准备好的顺序让列车到达主要轨道,我现在很高兴。

到目前为止,这是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <readline/readline.h>
#include <unistd.h>

struct Train{
    pthread_t thread;
    pthread_cond_t granted;

    int train_number;
    int loading_time;
    int crossing_time;

    int priority;
    char direction;
    char state;

}*newTrain;

struct Train *trains[3];

pthread_mutex_t track       = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  dispatcher  = PTHREAD_COND_INITIALIZER;

char *loading = "L";
char *ready = "R";
char *granted_t = "T";
char *gone = "G";
char *acknowledged_gone = "X";

void *dispatcher_function(void *train_count) {
    int count = *((int *) train_count);
    int trains_remaining = count; 

    /* Check for ready trains until all trains have left the station */
    while (trains_remaining > 0) {
        pthread_mutex_lock(&track);

        int t_granted = 0;
        int next = -1;

        for(int i = 0; i < count; i++){
            if (strcmp(&trains[i]->state, "T") == 0)
                t_granted = 1;
            if (strcmp(&trains[i]->state, "R") == 0)
                next = i;
            if (strcmp(&trains[i]->state, "G") == 0){
                trains_remaining--;
                trains[i]->state = *acknowledged_gone;
            }
        }

        /* Train was granted access to station wait for it to leave */
        if (t_granted) {
            pthread_cond_wait(&dispatcher, &track);
        }

        /* No trains in station. Wait for train */
        if (next == -1) {
            pthread_cond_wait(&dispatcher, &track);
        }

        /* Train ready in station grant next train track permission*/
        else{
            trains[next] -> state = *granted_t;
            pthread_cond_signal(&(trains[next] -> granted));
        }
        pthread_mutex_unlock(&track);
    }
    pthread_exit(0);
}

void *train_function(void* train) {
    struct Train *self = (struct Train*)train;

    /* Each train has its own cond var */
    pthread_cond_init(&self->granted, NULL);

    /* Load train */
    usleep(self -> loading_time);

    /* Lock track */
    pthread_mutex_lock(&track);

    /* Train ready */
    self -> state = *ready;
    printf("Train %d is ready to go %c\n", self -> train_number, self -> direction);

    /* Signal dispatcher */
    pthread_cond_signal(&dispatcher);

    while(strcmp(&self->state, "T") != 0)
        pthread_cond_wait(&(self->granted), &track);

    /* Use the track */
    printf("Train %d is ON the main track going %c\n", self -> train_number, self -> direction);
    usleep(self -> crossing_time);
    self -> state = *gone;
    printf("Train %d is OFF the main track after going %c\n", self -> train_number, self -> direction);

    pthread_cond_signal(&dispatcher);
    pthread_mutex_unlock(&track);

    pthread_exit(0);
}

int main() {

    FILE *ptr_file;
    char buff[10];
    int train_count = 0;
    char *train;
    char line[15];
    pthread_t train_threads[3];
    pthread_t dispatcher_thread;

    ptr_file = fopen("./trains.txt", "r");
    if (!ptr_file) 
    {
        perror("fopen for trains.txt failed");
        exit(EXIT_FAILURE);
    }

    /* Create train for each line of file */
    while (fgets(buff,10, ptr_file)!=NULL) {
        train = (char*)malloc(10 * sizeof(char));

        /* Build train string */
      sprintf(line, "%d:", train_count);
        strcat(line, buff);
        strcpy(train, line);

        /* Parse train information */
        int line_number = atoi(strtok(train, ":,"));
        char *direction = strtok(NULL,":,");
        int loading_time = atoi(strtok(NULL, ":,"));
        int crossing_time = atoi(strtok(NULL, ":,"));

        /* Create trains */
        newTrain = (struct Train *) malloc(sizeof(struct Train));
        newTrain -> train_number = line_number;
        newTrain -> crossing_time = crossing_time;
        newTrain -> loading_time = loading_time;
        newTrain -> direction = *direction;
        newTrain -> state = *loading;

        if(pthread_create(&train_threads[train_count], NULL, &train_function, (void *) newTrain))
        {
            perror("pthread create failed");
            exit(EXIT_FAILURE);
        }

        trains[line_number] = newTrain;
        train_count++;
    }
    fclose(ptr_file);

    /* Create dispatcher */
    if(pthread_create(&dispatcher_thread, NULL, &dispatcher_function, (void *) &train_count))
    {
        perror("pthread create failed");
        exit(EXIT_FAILURE);
    }

    /* Wait for dispatcher to finish */
    pthread_join(dispatcher_thread, NULL);
    printf("all done");

    free(train);
    for (int i = 0; i < train_count; i++) {
        free(trains[i]);
    }
    exit(EXIT_SUCCESS);
}

这是trains.txt输入文件:

e:10,6
W:5,7
E:3,10

这是我运行时得到的输出:

Train 0 is ready to go e
Train 0 is ON the main track going e
Train 0 is OFF the main track after going e
Train 2 is ready to go E
Train 1 is ready to go W
Train 2 is ON the main track going E
Train 2 is OFF the main track after going E
Train 1 is ON the main track going W
Train 1 is OFF the main track after going W

在所有列车离开车站后,该程序现在悬挂。如此接近我必须遗漏一些东西。

1 个答案:

答案 0 :(得分:1)

在您的程序可以被推断之前,您需要纠正几个错误:

trains.state是一个char对象 - 它不是一个字符串,因为它不一定跟一个空终止符。这意味着您无法像在多个地方那样将地址传递给strcmp() - 而不是:

if (strcmp(&trains[i]->state, "T") == 0)

使用:

if (trains[i]->state == 'T')

(注意字符常量的单引号而不是字符串常量。)

free(train)末尾不能train_function(),因为调度员一直在运行,需要访问所有列车结构。相反,请在调度程序退出后将其全部释放到main()

列车条件变量granted未初始化。您无法复制pthread_cond_t个变量 - 而是使用pthread_cond_init

void *train_function(void* train)
{
    struct Train *self = (struct Train*)train;

    /* Each train has its own cond var */
    pthread_cond_init(&self->granted, NULL);

train_function()的开头修改self->state而不保持锁定,这意味着它可以与调度程序一起读取相同的对象。您需要锁定修改:

/* Load train */
pthread_mutex_lock(&track);
self -> state = *loading;
pthread_mutex_unlock(&track);
usleep(self -> loading_time);

/* Lock track */
pthread_mutex_lock(&track);

/* Train ready */
self -> state = *ready;
printf("Train %d is ready to go %c\n", self -> train_number, self -> direction);

(在启动其他线程之前,state中所有列车的main()初始化为“加载”,可以避免第一次锁定。

此外,你不能假设你等待的条件是真的,只是因为pthread_cond_wait()已经醒来。即使未发出信号,pthread_cond_wait()也可以返回;它返回只意味着可能已经发出信号。这意味着,例如在train_function中,您需要使用pthread_cond_wait()代替while来围绕if

while (self->state != 'T')
    pthread_cond_wait(&self->granted, &track);

你需要在调度员中做一些类似的事情,你会发现赛道上有火车。

这实际上是你问题的核心 - 当你在这一点上被唤醒时:

if (t_granted) {
    pthread_cond_wait(&dispatcher, &track);
    trains_remaining--;
}

假设它是因为你在轨道上看到的火车现在已经完成了。但这并不一定正确 - 您可能已经通过 next 列车完成装载发出信号。这意味着你将绕过循环并再次在轨道上看到相同的列车,并且递减trains_remaining次。

因此,当您在赛道上看到火车时,您不能只调整trains_remaining,因为您可能会看到同一列火车两次 - 或者根本不看火车,如果火车很快就会“消失”。 / p>

相反,您应该在第一次看到某个列车处于“离开”状态时递减trains_remaining。您可以通过添加一个新的状态来实现这一点,该状态是调度员在看到它“消失”之后设置火车的,例如:

    for (int i = 0; i < count; i++) {
        if (trains[i]->state == 'T')
            t_granted = 1;
        if (trains[i]->state == 'R')
            next = i;
        if (trains[i]->state == 'G') {
            trains_remaining--;
            trains[i]->state = 'X'; /* acknowledged gone */
        }
    }