Ваши производители производят всю строку за один l oop, а потребитель потребляет всю строку за один l oop, так что максимум у вас есть один потребитель и один производитель l oop, выполняющий всю работу .
Вместо этого рассмотрите следующий подход:
static volatile int buffer_data; /* A single-character buffer */
static sem_t buffer_produced;
static sem_t buffer_consumed;
void *consumer(void *unused) {
while (1) {
/* Wait for data in the buffer. */
sem_wait(&buffer_produced);
/* sleep(1); */
/* End of data? */
if (buffer_data == EOF) {
/* If there are other consumers, wake up next one */
sem_post(&buffer_produced);
break;
}
/* Consume it. */
fputc(stdout, buffer_data);
fflush(stdout);
sem_post(&buffer_consumed);
}
return NULL;
}
Потребитель ожидает семафор buffer_produced
(который должен быть инициализирован нулем). Затем он проверяет произведенные данные, buffer_data
, который представляет собой один символ, или EOF
, чтобы указать, что данных больше нет.
Если есть более одного потребителя, нам нужно опубликовать buffer_produced
семафор, чтобы каждый потребитель тоже видел EOF
. Затем поток завершается. (Все потребители будут выходить более или менее группами, при этом произведено EOF
.)
Если данные не EOF
, потребитель распечатывает их на стандартный вывод. Поскольку стандартный вывод по умолчанию буферизуется, мы fflush(stdout)
, чтобы напечатанный символ доставлялся на экран (или канал или файл, если стандартный вывод перенаправлен на что-то).
Наконец, потребитель отправляет сообщение на buffer_consumed
семафор, чтобы указать производителям, что буфер теперь должен быть заполнен следующим символом данных.
Закомментированный sleep(1);
задержит печать каждого символа на одну секунду, поэтому вы увидите символы появляются с интервалом в одну секунду.
Производители, которые создают строковое сообщение, могут быть примерно такими:
static const char message_str[] = "Hello, world!\n";
static size_t message_pos = 0;
void *producer(void *) {
int c;
do {
sem_wait(&buffer_consumed);
c = message_str[message_pos];
if (c != '\0') {
buffer_data = c;
message_pos++;
} else {
buffer_data = EOF;
}
sem_post(&buffer_produced);
} while (c != EOF);
return NULL;
}
Производитель ожидает buffer_consumed
семафор, который должен быть инициализирован 1 По сути, когда buffer_consumed == 1
, это означает, что буфер пуст.
Затем он проверяет следующий символ создаваемой строки. Если это конец строки, мы преобразуем его в EOF
. (Почему? На самом деле, нет причин; кроме того, что таким образом вы можете легко изменить производителя для создания двоичных данных, включая встроенные нулевые байты.)
Производитель устанавливает buffer_data
на символ или EOF
, и размещает семафор buffer_produced
, чтобы потребитель мог его использовать.
Если это было EOF
, производитель готов и может выйти.
Обратите внимание, что когда message_pos
указывает на нулевой символ конца строки, '\0'
, мы не увеличиваем его. Это сделано для того, чтобы, если у вас несколько производителей, они тоже увидят нулевой символ конца строки (создавая «другой» EOF
) и завершат работу.
Если производителей на N
больше, чем потребителей, то после возвращения последнего потребителя основной поток должен отправить N
семафор buffer_consumed
, чтобы все производители проснулись и вышли. (Итак, если у вас есть 1 потребитель и 4 производителя, вам нужно sem_post(&buffer_consumed)
3 раза.)
Если вы изучите поток программы, вы увидите, что каждый семафор никогда не отправляется более одного раза , до его ожидания, за исключением самого конца, когда потоки завершаются без производства / потребления каких-либо данных.