#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