«Проблема пересечения поездов с мьютексом» - получение NULL на последних 2 линиях - PullRequest
0 голосов
/ 15 ноября 2018

Эй, у меня проблемы с исправлением ошибки, из-за которой некоторые нежелательные значения NULL отображались в моем конечном выводе.Когда вы запускаете программу, перед запуском введите строку, такую ​​как 'nnseeeewwwew' в командную строку.

Вот код

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

/* mutex */
pthread_mutex_t thisMutex;

/* Condition Variables */
sem_t nQueue, eQueue, sQueue, wQueue;
int nFirst, sFirst, eFirst, wFirst;
int done = 0;

/* Thread Prototypes */
void *bats(void *);
void *checking(void *);

/* Prototypes */
void arrive(int num, char *dir);
void cross();
void leave(int num, char *dir);
void check();

/* Global pointer to argv */
char *directions = NULL;

int main(int argc, char *argv[])
{
    int i = 0,
    n = 0,
    s = 0,
    e = 0,
    w = 0;

    directions = argv[1];
    while(  argv[1][i] == 'n' || argv[1][i] == 'e' ||
          argv[1][i] == 's' || argv[1][i] == 'w' )
    {
        if(argv[1][i] == 'n'){n++;}
        if(argv[1][i] == 's'){s++;}
        if(argv[1][i] == 'e'){e++;}
        if(argv[1][i] == 'w'){w++;}
        i++;
    }


    pthread_mutex_init(&thisMutex, NULL);
    sem_init(&nQueue, 0, n);
    sem_init(&sQueue, 0, s);
    sem_init(&eQueue, 0, e);
    sem_init(&wQueue, 0, w);
    nFirst = sFirst = eFirst = wFirst = 0;

    pthread_t tid[i];
    pthread_t checker;

    pthread_create(&checker, NULL, checking, NULL);

    for(int j = 0; j < i; j++)
    { pthread_create(&tid[j], NULL, bats, (void*) &j); }

    for(int j = 0; j < i; j++)
    { pthread_join(tid[j], NULL); }

    done = 1;

    pthread_join(checker, NULL);

    pthread_mutex_destroy(&thisMutex);
    sem_destroy(&nQueue);
    sem_destroy(&sQueue);
    sem_destroy(&wQueue);
    sem_destroy(&eQueue);

    return 0;
}


void *checking(void *arg)
{
    while( done == 0 )
    {
        if( nFirst == 1 || sFirst == 1 ||
           eFirst == 1 || wFirst == 1 )
            check();
    }
    exit(0);
}

void *bats(void *arg)
{
    int index = *(int *)arg;
    char *dir;
    switch (directions[index])
    {
        case 'n':
            dir = "North";
            break;
        case 's':
            dir = "South";
            break;
        case 'e':
            dir = "East";
            break;
        case'w':
            dir = "West";
            break;
    }
    arrive(index, dir);
    leave(index, dir);
    _exit(0);
}

/* Functions */
void arrive(int num, char *dir)
{
    printf("BAT %d from %s arrives at crossing\n", num, dir);
    if(strcmp(dir,"North")== 0)
    {
        sem_wait(&nQueue);
        nFirst = 1;
        while(eFirst == 1){ /* do nothing */ }
        cross();
        nFirst = 0;
        sem_post(&nQueue);
    }
    else if(strcmp(dir,"West")== 0)
    {
        sem_wait(&wQueue);
        wFirst = 1;
        while(nFirst == 1){ /* do nothing */ }
        cross();
        wFirst = 0;
        sem_post(&wQueue);
    }
    else if(strcmp(dir,"South")== 0)
    {
        sem_wait(&sQueue);
        sFirst = 1;
        while(wFirst == 1){ /* do nothing */ }
        cross();
        sFirst = 0;
        sem_post(&sQueue);
    }
    else if(strcmp(dir,"East")== 0)
    {
        sem_wait(&eQueue);
        eFirst = 1;
        while(sFirst == 1){ /* do nothing */ }
        cross();
        eFirst= 0;
        sem_post(&eQueue);
    }
}

void cross()
{
    pthread_mutex_lock(&thisMutex);
    sleep(1);
    pthread_mutex_unlock(&thisMutex);;
}

void leave(int num, char *dir)
{
    printf("BAT %d from %s leaving crossing\n", num, dir);
}

void check()
{
    if( nFirst == 1 && sFirst == 1 &&
       eFirst == 1 && wFirst == 1 )
    {
        eFirst = 0;
        sleep(1);
        eFirst = 1;
    }
}

Цель этого задания -

  1. BAT, прибывающих с той же линии направления вверх за первой BATуже на пересечении;

  2. BAT, прибывающие справа, всегда имеют право проезда (если только ожидающая BAT не получит сигнал о выходе);

  3. Необходимо предотвратить тупик

  4. Необходимо предотвратить голодание

После запуска кода вы увидите последние две строкииметь «NULL» для направления прибытия BAT.Нужна помощь, чтобы решить эту проблему

Спасибо!

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Ваша программа имеет неопределенное поведение, потому что вы передаете указатель на локальную переменную в вашу функцию потока.В функции bats при разыменовании указателя вы можете получить значение мусора:

   for(int j = 0; j < i; j++)
    { 
      pthread_create(&tid[j], NULL, bats, (void*) &j); 
    }

j локально внутри для цикла.

Вы можете выделить память для хранениязначение для переменной int:

   for(int j = 0; j < i; j++)
    { 
      int* var = malloc(sizeof(int));
      *var = j;
      pthread_create(&tid[j], NULL, bats, var); 
    }

не забудьте освободить эту память в bats функция:

void *bats(void *arg)
{
    int index = *(int *)arg;
    //...
    leave(index, dir);
    free (arg); // <--
    pthread_exit(NULL); // instead of _exit(0);
}
0 голосов
/ 15 ноября 2018
  1. dir - указатель на символ. Таким образом, вы не можете напрямую использовать знак равенства, такой как if( dir == "North") Что вам нужно, это if ((strcmp(dir,"North")== 0)

  2. в цикле ниже,

    while(  argv[1][i] == 'n' || argv[1][i] == 'e' ||
          argv[1][i] == 's' || argv[1][i] == 'w' )
    {
        i++;
        if(argv[1][i] == 'n'){n++;}
        if(argv[1][i] == 's'){s++;}
        if(argv[1][i] == 'e'){e++;}
        if(argv[1][i] == 'w'){w++;}
    }
    

Вы увеличиваете i первым внутри цикла. Итак, для последнего элемента в массиве argv[1][i] вы читаете ненужные значения. Приращение i должно быть последним оператором в цикле. то есть после if(argv[1][i] == 'w'){w++;}

...