классическая проблема производителя-потребителя в C.
#include <semaphore.h>
#include <stdio.h>
#include <pthread.h>
int buf = 0;
sem_t *mutex, *full, *empty;
void *producer(void *arg) {
while (1) {
sem_wait(empty); // Wait for an empty slot
sem_wait(mutex); // Acquire mutex for critical section
buf++;
printf("Item produced. Count: %d\n", buf);
sem_post(mutex); // Release mutex
sem_post(full); // Signal that an item is available
}
return NULL;
}
void *consumer(void *arg) {
while (1) {
sem_wait(full); // Wait for an item to be available
sem_wait(mutex); // Acquire mutex for critical section
buf--;
printf("Item consumed. Count: %d\n", buf);
sem_post(mutex); // Release mutex
sem_post(empty); // Signal that an empty slot is available
}
return NULL;
}
int main() {
pthread_t producer_thread, consumer_thread;
// Initialize semaphores
mutex = sem_open("/mutex", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 1);
full = sem_open("/full", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0);
empty = sem_open("/empty", O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 10);
// Create producer and consumer threads
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
// Join threads (never reached in this example due to infinite loop in threads)
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
// Close and unlink semaphores (optional, but good practice)
sem_close(mutex);
sem_close(full);
sem_close(empty);
sem_unlink("/mutex");
sem_unlink("/full");
sem_unlink("/empty");
return 0;
}
и вот некоторые результаты:
Item consumed. Count: 1926
Item consumed. Count: 1925
Item consumed. Count: 1924
Item produced. Count: 1933
Item produced. Count: 1924
Item consumed. Count: 1923
Item consumed. Count: 1924
Item consumed. Count: 1923
Item consumed. Count: 1922
Item consumed. Count: 1921
Item consumed. Count: 1920
я сделал пустое значение равным 10, поэтому ожидал, что это будет предел буфера. Но почему выход превышает лимит?
делал все по учебнику. на macbook air m1, поэтому sem_init не работает, вместо этого используйте sem_open
1. Не могу повторить. 2. Вы пришли к нам с просьбой исправить ошибки; почему ты не сделал то же самое с ОС? Проверяйте свои звонки на наличие ошибок! 3. Почему вы подаете в суд на sem_open, а не на sem_init? В результате последующие запуски вашей программы завершаются сбоем (из-за сбоя sem_open, поскольку семафор уже открыт, и вы не проверяете наличие ошибок).
Примечание: вашей программе должна быть предоставлена возможность корректно завершить работу. Если вам придется убить его, чтобы остановить, то он оставит семафоры позади и не сможет открыть их (из-за O_CREAT | O_EXCL
) в следующий раз, если вы не очистите их отдельно.
Или, как минимум, unlink
каждый семафор, как только вы его открываете, поскольку вам не нужно (или не хочется!) разделять их между процессами. Это не помешает программе использовать их, но позволит избежать их оставления, если только программа не будет завершена между ее открытием и отключением.
спасибо, братан, проблема была в отключении связи. Первая попытка моего кода не закрыла и не отключила семафоры, поэтому они остались в моей ОС. и все последующие попытки продолжают использовать существующие sems. вот почему buff вне диапазона. Решил проблему, запустив специальную программу, которая отсоединяла эти сэмы.
Я рад, что помог. Но отмечу, что, как я уже заметил, открытие семафоров с помощью O_CREAT | O_EXCL
означает, что, если они уже существуют, их открытие не удастся - что ваша программа не пытается обнаружить. Таким образом, вы не используете существующие семафоры. Если семафоры с заданными именами уже существуют, ваша программа вообще не использует семафоры. Все эти операции с семафором терпят неудачу, и программа не обращает на это внимания.
Основная проблема сценария заключается в том, что его операции с семафорами не работают, но программа не обращает на это внимания. Функции sem_open
, sew_wait
и sem_post
, среди прочих, передают информацию об успехе/неуспехе через свои возвращаемые значения, как и многие другие функции C. Эту информацию необходимо отслеживать и принимать соответствующие меры, чтобы избежать неправильного поведения программы.
И это не то, что можно добавить позже. Вам следует сделать это при первом написании кода, потому что
именно тогда вы думаете о требуемом поведении и лучше всего готовы принять решение о соответствующей обработке различных ошибок.
именно тогда ваш код, скорее всего, будет содержать ошибки, правильная обработка которых поможет вам распознать и диагностировать их.
на практике вам, вероятно, не придется возвращаться позже, чтобы выполнить всю необходимую обработку ошибок.
В этом случае отсутствие правильной обработки ошибок для ваших вызовов sem_open()
является ошибкой, поскольку такие сбои в вашем случае весьма вероятны, и когда они происходят, ваша программа не получает требуемого поведения семафора. Отсутствие правильной обработки ошибок для вызовов sem_wait()
и sem_post()
приводит к тому, что проблема проявляется в виде загадочного неправильного поведения, а не в виде информативной диагностики.
С учетом вышесказанного, одной из проблем является то, что ваша программа неизбежно не сможет должным образом управлять своими семафорами. Когда вы используете именованные семафоры, вы должны
принять эффективные меры для обеспечения того, чтобы они использовались только теми процессами, которые предназначены для их использования.
Открытие с помощью флагов O_CREAT
и O_EXCL
не позволяет двум процессам использовать один и тот же семафор, что помогает, но создает возможность блокировки других процессов от использования семафоров с одинаковыми именами. Это обоюдоострый подход для вашей конкретной программы.
соответствующим образом управлять их видимостью и сроком службы.
В основном это означает sem_unlink()
открыть их в наиболее подходящее время, то есть как можно скорее после того, как вы будете уверены, что все процессы, которые захотят открыть конкретный семафор, сделали это. Как и удаление связи с обычным файлом, удаление семафора не уничтожает его. Это только отделяет его от пространства имен, так что он не может быть открыт другими процессами. Те, у кого он уже открыт или получил доступ с помощью других средств, кроме sem_open()
, могут продолжать использовать его до тех пор, пока не закроют его явно или путем прекращения. Система автоматически очистит отсоединенные файлы SE, если они больше не открываются ни одним процессом.
Написание кода обработки ошибок для большого количества вызовов функций может оказаться утомительным, а обновление большого количества кода обработки ошибок может быть совершенно болезненным. В результате люди довольно часто используют макросы для обработки всех этих ошибок. Это дает дополнительное преимущество, заключающееся в том, что основное поведение программы не скрывается за большим количеством шаблонной обработки ошибок. Есть много способов проработать детали, один из них:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <errno.h>
int buf = 0;
sem_t *mutex, *full, *empty;
#define warn_if (val, cond, context) do { \
if ((val) cond) { \
perror("warning: " context); \
} \
} while (0)
#define exit_if (val, cond, context) do { \
if ((val) cond) { \
perror(context); \
exit(1); \
} \
} while (0)
#define do_or_die(func, paren_args) { \
exit_if ((func) paren_args, == -1, #func); \
}
#define pt_do_or_die(func, paren_args) { \
exit_if (errno = (func) paren_args, != 0, #func); \
}
sem_t *create_semaphore(const char *name, unsigned int initial_val) {
sem_t *sem = sem_open(name, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, initial_val);
exit_if (sem, == SEM_FAILED, "sem_open");
warn_if (sem_unlink(name), == -1, "sem_unlink");
return sem;
}
void *producer(void *arg) {
while (1) {
do_or_die(sem_wait, (empty)); // Wait for an empty slot
do_or_die(sem_wait, (mutex)); // Acquire mutex for critical section
buf++;
printf("Item produced. Count: %d\n", buf);
do_or_die(sem_post, (mutex)); // Release mutex
do_or_die(sem_post, (full)); // Signal that an item is available
}
return NULL;
}
void *consumer(void *arg) {
while (1) {
do_or_die(sem_wait, (full)); // Wait for an item to be available
do_or_die(sem_wait, (mutex)); // Acquire mutex for critical section
buf--;
printf("Item consumed. Count: %d\n", buf);
do_or_die(sem_post, (mutex)); // Release mutex
do_or_die(sem_post, (empty)); // Signal that an empty slot is available
}
return NULL;
}
int main() {
pthread_t producer_thread;
pthread_t consumer_thread;
// Initialize semaphores
mutex = create_semaphore("/mutex", 1);
full = create_semaphore("/full", 0);
empty = create_semaphore("/empty", 10);
// Create producer and consumer threads
pt_do_or_die(pthread_create, (&producer_thread, NULL, producer, NULL));
pt_do_or_die(pthread_create, (&consumer_thread, NULL, consumer, NULL));
// Join threads (never returns in this example due to infinite loop in threads)
pt_do_or_die(pthread_join, (producer_thread, NULL));
pt_do_or_die(pthread_join, (consumer_thread, NULL));
// Close semaphores (optional, but good practice)
warn_if (sem_close(mutex), == -1, "sem_close");
warn_if (sem_close(full), == -1, "sem_close");
warn_if (sem_close(empty), == -1, "sem_close");
return 0;
}
Вы не тестируете возвращаемые значения ни одной из ваших операций с семафором. Если бы какой-либо из них потерпел неудачу, вы бы это обычно определили именно так.