Я только начал писать многопоточность на C и не совсем понимаю, как это реализовать. Я пишу код, который считывает входной файл и помещает его в массив структуры буфера. Когда в буфере больше нет свободного места,request_t
заблокирован в ожидании свободного места. Управляется потокомLift_R
. Остальные нитки подъемника 1-3 работают.lift()
и он записывает то, что находится в буфере, в выходной файл в зависимости от количестваint sec
.sec
а такжеsize
и заданные значения через командную строку. Это освободит место для запроса на продолжение чтения ввода.
Может ли кто-нибудь помочь мне, как правильно реализовать эти функции. Я знаю, что есть и другие вопросы, связанные с этим, но я хочу, чтобы мой код отвечал определенным условиям.
(ПРИМЕЧАНИЕ: лифт работает в режиме 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;
Между чтением вашего кода и вопросом я вижу большой концептуальный пробел. В коде есть некоторые технические проблемы (например, вы никогда не закрываете); и сложная для понимания последовательность.
Итак, этот паттерн:
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);
Итак, начните с небольшого переосмысления того, что вы делаете. Похоже, у вас есть лифт, которому нужно принимать входящие запросы, возможно, сортировать их, а затем обрабатывать. Скорее всего, «навсегда».
Вероятно, это означает отсортированную очередь; однако он не отсортирован по обычным критериям. Лифт перемещается по зданию в обоих направлениях, но это позволяет минимизировать изменение направления. Это включает обход очереди как в порядке (>, <), так и в текущем направлении. Скорее всего, вы захотите, чтобы запрос просто оценил график подъема и определил, куда вставить новый запрос. График подъема будет однонаправленным списком того, куда подъемник пойдет дальше. И, возможно, правило, согласно которому список сверяется со своим списком только тогда, когда он останавливается на определенном этаже.
Таким образом, запрос может заблокировать график, изменить его, чтобы отразить новый запрос, а затем разблокировать.
Лифт может просто:
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 () может быть немного непростым ....