Мьютекс буфера и переменные условия в C - PullRequest
0 голосов
/ 20 апреля 2020

Я только начал писать многопоточность в C и не совсем понимаю, как это реализовать. Я пишу код, который читает входной файл и помещает в массив структуры буфера. Когда в буфере больше нет свободного места, request_t блокируется в ожидании доступного пространства. Управляется нитью Lift_R. Другие потоки с поддержкой 1-3 работают lift() и записывают данные в буфере в выходной файл в зависимости от числа int sec. sec и size и заданные значения через командную строку. Это освободит место для запроса о продолжении чтения ввода.

Может кто-нибудь помочь мне с тем, как правильно реализовать эти функции. Я знаю, что есть другие вопросы, связанные с этим, но я хочу, чтобы мой код удовлетворял указанным c условиям.

(ПРИМЕЧАНИЕ: Лифт работает в FIFO и потоки используют взаимное исключение)

Это то, что я написал до сих пор, я еще не реализовал никаких условий ожидания или FIFO, в настоящее время я сосредоточен на записи в файл и отладке и скоро получаю ожидание и сигнал.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h> 
#include "list.h"
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; //declare thread conditions
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; //declare mutex
int sec; //time required by each lift to serve a request
int size; //buffer size
buffer_t A[];
write_t write;
void *lift(void *vargp)

{
    pthread_mutex_lock(&lock);
    FILE* out;
    out = fopen("sim_output.txt", "w");
    //gather information to print
    if (write.p == NULL) //only for when system begins
    {
        write.p = A[1].from;
    }
    write.rf = A[1].from;
    write.rt = A[1].to;
    write.m = (write.p - A[1].from) + (A[1].to - A[1].from);
    if (write.total_r == NULL) //for when the system first begins
    {
        write.total_r = 0;
    }
    else
    {
        write.total_r++;
    }
    if (write.total_m == NULL)
    {
        write.total_m = write.m;
    }
    else
    {
        write.total_m = write.total_m + write.m;
    }
    write.c = A[1].to;
    //Now write the information
    fprintf(out, "Previous position: Floor %d\n", write.p);
    fprintf(out, "Request: Floor %d to Floor %d\n", write.rf, write.rt);
    fprintf(out, "Detail operations:\n");
    fprintf(out, "    Go from Floor %d to Floor %d\n", write.p, write.rf);
    fprintf(out, "    Go from Floor %d to Floor %d\n", write.rf, write.rt);
    fprintf(out, "    #movement for this request: %d\n", write.m);
    fprintf(out, "    #request: %d\n", write.total_r);
    fprintf(out, "    Total #movement: %d\n", write.total_m);
    fprintf(out, "Current Position: Floor %d\n", write.c);
    write.p = write.c; //for next statement
    pthread_mutex_unlock(&lock);
    return NULL;
}


void *request_t(void *vargp)
{
    pthread_mutex_lock(&lock); //Now only request can operate
    FILE* f;
    FILE* f2;
    f = fopen("sim_input.txt", "r");
    if (f == NULL)
    {
        printf("input file empty\n");
        exit(EXIT_FAILURE);
    }
    f2 = fopen("sim_output.txt", "w");

    int i = 0;
    for (i; i < size; i++)
    {
        //read the input line by line and into the buffer
        fscanf(f, "%d %d", &A[i].from, &A[i].to);\
        //Print buffer information to sim_output
        fprintf(f2, "----------------------------\n");
        fprintf(f2, "New Lift Request from Floor %d to Floor %d \n", A[i].from, A[i].to);
        fprintf(f2, "Request No %d \n", i);
        fprintf(f2, "----------------------------\n");
    }
    printf("Buffer is full");
    fclose(f);
    fclose(f2);
    pthread_mutex_unlock(&lock);
    return NULL;
}


void main(int argc, char *argv[]) // to avoid segmentation fault
{
    size = atoi(argv[0]);
    if (!(size >= 1))
    {
        printf("buffer size too small\n");
        exit(0);
    }
    else
    {
        A[size].from = NULL;
        A[size].to = NULL;
    }
    sec = atoi(argv[1]);
    pthread_t Lift_R, lift_1, lift_2, lift_3;

    pthread_create(&Lift_R, NULL, request_t, NULL);

    pthread_join(Lift_R, NULL);

    pthread_create(&lift_1, NULL, lift, NULL);

    pthread_join(lift_1, NULL);

    pthread_create(&lift_2, NULL, lift, NULL);

    pthread_join(lift_2, NULL);

    pthread_create(&lift_3, NULL, lift, NULL);

    pthread_join(lift_3, NULL);

}

А здесь это файлы структуры:

#include <stdbool.h>

typedef struct Buffer
{
    int from;
    int to;
}buffer_t; //buffer arrary to store from and to values from sim_input

typedef struct Output
{
    int l; //lift number
    int p; //previous floor
    int rf; //request from
    int rt; //request to
    int total_m; //total movement
    int c; // current position
    int m; //movement
    int total_r; //total requests made
}write_t;

1 Ответ

1 голос
/ 20 апреля 2020

Между чтением вашего кода и вопросом я вижу большой концептуальный разрыв. В коде есть некоторые технические проблемы (например, вы никогда не закрываете); и трудно следовать последовательности.

Итак, этот шаблон:

  pthread_create(&x, ?, func, arg);
  pthread_join(x, ...);

Может быть заменен на:

  func(arg);

, так что вы действительно не многопоточны в все; это точно так, как если бы:

void main(int argc, char *argv[]) // to avoid segmentation fault
{
    size = atoi(argv[0]);
    if (!(size >= 1))
    {
        printf("buffer size too small\n");
        exit(0);
    }
    else
    {
        A[size].from = NULL;
        A[size].to = NULL;
    }
    sec = atoi(argv[1]);

    request_t(0);
    lift(0);
    lift(0);
    lift(0);
}

и, зная это, я надеюсь, что вы видите бесполезность в:

pthread_mutex_lock(&lock);
....
pthread_mutex_unlock(&lock);

Итак, начните с небольшого переосмысления того, что вы делают. Похоже, у вас есть подъемное устройство, которое должно принимать входящие запросы, возможно, сортировать их, а затем обрабатывать. Вероятно, навсегда.

Это, вероятно, означает отсортированную очередь; однако один не отсортирован по обычным критериям. Лифт пересекает здание в обоих направлениях, но означает минимизацию изменений в направлении. Это включает в себя обход очереди как с порядком (>, <), так и с текущим направлением. Вы, вероятно, захотите запрос просто оценить график лифта и определить, куда вставить новый запрос. График лифта будет однонаправленным списком того, куда лиса пойдет дальше. И, возможно, правило, что список обращается только к своему списку, поскольку он останавливается на заданном этаже. </p>

Таким образом, Запрос может взять блокировку графа, изменить его, чтобы отразить новый запрос, а затем разблокировать его.

Подъемник может просто:

  while (!Lift_is_decommissioned) {
        pthread_mutex_lock(&glock);
        Destination = RemoveHead(&graph);
        pthread_mutex_unlock(&glock);
        GoTo(Destination);
  }

И запрос может быть:

  pthread_mutex_lock(&glock);

  NewDestination = NewEvent.floorpressed;
  NewDirection   = NewEvent.floorpressed > NewEvent.curfloor ? Up : Down;
  i = FindInsertion(&graph, NewDestination, NewDirection);
  InsertAt(&graph, i, NewDestination);

  pthread_mutex_unlock(&glock);

Что может быть немного удивительно, что нет никакой разницы между нажатием " перейти на пол "изнутри лифта, и" Я хочу подняться здесь сейчас "снаружи лифта.

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

FindInsertion () может быть немного неопрятным ....

...