Search
Previously we covered pthread_mutex_lock and pthread_mutex_unlock. There is also a third locking mechanism: pthread_mutex_trylock. Try lock will not wait until the lock is available, it will return immediately. It will return zero if it acquired the lock, else something else if not (specifically an error code, most commonly a number meaning “Lock is busy”).
Exercise 1: Modify the code from last week.
Change the locks to use pthread_mutex_trylock instead of pthread_mutex_lock. If the lock cannot be acquired, wait/sleep and try again. Sleeping is available in #include <unistd.h> library as the functions sleep(int seconds) and usleep(int microseconds).
#include <unistd.h>
sleep(int seconds)
usleep(int microseconds)
Conditional variables are often used in multithreaded programming to synchronize threads. They allow threads to wait for certain conditions to be met. A conditional variable works with a mutex to ensure safe access and modification of shared resources.
pthread_cond_t
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex)
int pthread_cond_signal(pthread_cond_t *cond)
Different to a mutex. It should be used to multithread/multiprocess signalling rather than shared resource protection. One should see it as an n-member resource access or just as a signalling tool. It does not guarantee any exclusivity (only in a simple binary configuration).
It is implemented in the POSIX library semaphore.h with docs here
semaphore.h
Threads are obviously used where we want multiple things to happen at the same time. Therefore they are commonly employed for slow processes like in the following schemes:
The final layout is called a producer-consumer model.
But is this a good idea? Hint: What happens if no locks are available? Think about how we can solve this.
Exercise 2: Create 3 threads which will generate a random integer *once per second*. The threads will loop forever. These will be our producers. They will safely put the random number into one global buffer. Use #include <stdlib.h> and the function int rand(). There will then be three other threads, our consumers, which will check if there is data in the buffer. If so, they will grab the result, and calculate if it is a prime number, and output the result.
#include <stdlib.h>
int rand()
int is_prime(int num) // yes=1, no=0 { if (num <= 1) return 0; if (num % 2 == 0 && num > 2) return 0; for(int i = 3; i < floor(sqrt(num)); i+= 2) { if (num % i == 0) return 0; } return 1; }
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> // Shared data int data_ready = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void* producer(void* arg) { sleep(1); // Simulate work pthread_mutex_lock(&mutex); data_ready = 1; // Data is ready printf("Producer: Data is ready!\n"); pthread_cond_signal(&cond); // Signal the consumer pthread_mutex_unlock(&mutex); return NULL; } void* consumer(void* arg) { pthread_mutex_lock(&mutex); while (data_ready == 0) { // Wait for the condition to be true printf("Consumer: Waiting for data...\n"); pthread_cond_wait(&cond, &mutex); } printf("Consumer: Consuming the data!\n"); pthread_mutex_unlock(&mutex); return NULL; } int main() { pthread_t prod_thread, cons_thread; pthread_create(&prod_thread, NULL, producer, NULL); pthread_create(&cons_thread, NULL, consumer, NULL); pthread_join(prod_thread, NULL); pthread_join(cons_thread, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }
#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h> sem_t S; void* thread(void* arg) { //wait sem_wait(&S); printf("\nEntered..\n"); //critical section sleep(4); //signal printf("\nJust Exiting...\n"); sem_post(&S); } int main() { sem_init(&S, 0, 1); pthread_t t1,t2; pthread_create(&t1,NULL,thread,NULL); sleep(2); pthread_create(&t2,NULL,thread,NULL); pthread_join(t1,NULL); pthread_join(t2,NULL); sem_destroy(&S); return 0; }