/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* producer_consumer.c :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: ruiferna +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/07 22:17:29 by ruiferna #+# #+# */ /* Updated: 2025/10/07 22:22:14 by ruiferna ### ########.fr */ /* */ /* ************************************************************************** */ #include #include #include // Para memset #include #include // Para usleep e write #include // Para gettimeofday #define BUFFER_SIZE 10 #define NUM_PRODUCERS 2 #define NUM_CONSUMERS 2 #define ITEMS_TO_PRODUCE 100 /** * @brief Estrutura de dados partilhada entre as threads. * * @param buffer O buffer partilhado (array de inteiros). * @param in Índice onde o próximo produtor irá escrever. * @param out Índice de onde o próximo consumidor irá ler. * @param count Número de itens atualmente no buffer. * @param items_produced Contador total de itens produzidos para controlo de fim. * @param mutex Mutex para garantir acesso exclusivo e atómico às * variáveis desta estrutura. */ typedef struct { int buffer[BUFFER_SIZE]; int in; int out; int count; int items_produced; pthread_mutex_t mutex; } shared_buffer_t; // Protótipos das funções das threads void *producer_thread(void *arg); void *consumer_thread(void *arg); int main(void) { // 1. Inicializar a estrutura partilhada shared_buffer_t shared_data; pthread_t producer_threads[NUM_PRODUCERS]; pthread_t consumer_threads[NUM_CONSUMERS]; // Limpar e inicializar os dados memset(&shared_data, 0, sizeof(shared_buffer_t)); // Inicializar o mutex (função permitida) if (pthread_mutex_init(&shared_data.mutex, NULL) != 0) { printf("Erro a inicializar o mutex\\n"); return 1; } printf("A iniciar threads...\n"); // 2. Criar as threads produtoras e consumidoras (função permitida) for (int i = 0; i < NUM_PRODUCERS; i++) { if (pthread_create(&producer_threads[i], NULL, producer_thread, &shared_data) != 0) { printf("Erro a criar thread produtora\\n"); return 1; } } for (int i = 0; i < NUM_CONSUMERS; i++) { if (pthread_create(&consumer_threads[i], NULL, consumer_thread, &shared_data) != 0) { printf("Erro a criar thread consumidora\\n"); return 1; } } // 3. Esperar que as threads produtoras terminem (função permitida) for (int i = 0; i < NUM_PRODUCERS; i++) { pthread_join(producer_threads[i], NULL); } printf("Produtores terminaram. A aguardar consumidores...\n"); // 4. Esperar que as threads consumidoras terminem // A lógica dentro da thread consumidora deve garantir que ela termina // quando não há mais itens a serem produzidos e o buffer está vazio. for (int i = 0; i < NUM_CONSUMERS; i++) { pthread_join(consumer_threads[i], NULL); } // 5. Destruir o mutex (função permitida) pthread_mutex_destroy(&shared_data.mutex); printf("Programa terminado.\n"); return (0); } // Lógica da Thread Produtora (a ser implementada) void *producer_thread(void *arg) { shared_buffer_t *shared = (shared_buffer_t *)arg; // TODO: Implementar a lógica de produção aqui. // Lembre-se do ciclo: // 1. Lock mutex. // 2. WHILE buffer está cheio: unlock, usleep, lock de novo. // 3. Produzir item. // 4. Unlock mutex. printf("Thread produtora a terminar.\n"); return (NULL); } // Lógica da Thread Consumidora (a ser implementada) void *consumer_thread(void *arg) { shared_buffer_t *shared = (shared_buffer_t *)arg; // TODO: Implementar a lógica de consumo aqui. // Lembre-se do ciclo: // 1. Lock mutex. // 2. WHILE buffer está vazio (e ainda há itens a produzir): unlock, usleep, lock de novo. // 3. Consumir item. // 4. Unlock mutex. printf("Thread consumidora a terminar.\n"); return (NULL); }