#include
#include
#include
char buff; // Споделен буфер (глобална променлива)
pthread_mutex_t mu; // Мутекс за взаимно изключване при достъп до buff
sem_t sem1, sem2; // Семафори за координация (пълно/празно)
void *producer(void* arg) {
char c;
for (c = 'a'; c <= 'z'; c++) {
// 1. Чакаме sem1 да стане > 0 (т.е. буферът да е свободeн за писане)
// В началото sem1 е 1, така че влизаме веднага.
sem_wait(&sem1);
// 2. Влизаме в критична секция (заключваме мутекса)
pthread_mutex_lock(&mu);
buff = c; // Записваме буквата в споделената памет
pthread_mutex_unlock(&mu); // Излизаме от критична секция
// 3. Сигнализираме на потребителя, че има данни (вдигаме sem2)
sem_post(&sem2);
}
pthread_exit(NULL);
}
void *consumer(void* arg) {
int i;
for (i = 0; i < 26; i++) {
// 1. Чакаме sem2 да стане > 0 (т.е. да има записани данни)
// В началото sem2 е 0, така че тази нишка ще чака Producer-а.
sem_wait(&sem2);
// 2. Влизаме в критична секция
pthread_mutex_lock(&mu);
printf("%c\n", buff); // Четем и отпечатваме
pthread_mutex_unlock(&mu); // Излизаме от критична секция
// 3. Сигнализираме на производителя, че сме готови за нова буква (вдигаме sem1)
sem_post(&sem1);
}
pthread_exit(NULL);
}
int main(int argc, char* argv[]) {
pthread_t t1, t2;
// Инициализиране на мутекса - това е динамично, но може и статично инициализиране
pthread_mutex_init(&mu, NULL);
// Инициализиране на семафорите:
// sem1 = 1: Разрешаваме на Producer да пише веднага (буферът е празен)
sem_init(&sem1, 0, 1);
// sem2 = 0: Забраняваме на Consumer да чете веднага (няма данни още)
sem_init(&sem2, 0, 0);
// Създаване на нишките
pthread_create(&t1, NULL, producer, NULL);
pthread_create(&t2, NULL, consumer, NULL);
// Изчакване на нишките да приключат работа
pthread_join(t1, NULL);
pthread_join(t2, NULL);
// Унищожаване на мутекса (добра практика, макар и незадължително тук)
pthread_mutex_destroy(&mu);
pthread_exit(NULL);
}
// Makefile
all:
gcc -O0 -o zad3 zad3.c -lpthread
start:
./zad3