Threads and synchronizations
My first threads
Write, in C, an application that creates N threads with pthread_create, where N is the first argument of the application. Each thread must write to the terminal "[tid] Hello, World i!!!", where:
- tid is the thread identifier returned by pthread_self,
- i is a number incremented by the main for each thread (0 for the first thread, 1 for the second and so on).
The main function has now to print Main quitting at the end of the function. Launch several times your application with 3 threads. Why, sometime, the application write the main quitting before the three hello worlds?
Start your application with 20 threads several times. Why, often, the application writes less than 20 times hello world?
Add a call to pthread_exit at the end of main. Why, now, the application systematically write 20 times hello world when you start it with 20 threads?
Ensure now that the application writes the main quitting after all the hello worlds.
A shared counter
First, add a global variable called counter initialized to 0 to your application. Then, ensure that each thread increments this variable after printing its hello world. Finally, modify the application in order to print the value of counter before the pthread_exit of the main.
When you run your application with 100 threads, why the printed counter is often smaller than 100? Modify your application in order to ensure that the counter is equal to the number of threads at end of the application.
Producer/consumer
In this exercise, we want to implement a producer/consumer. In detail, the main thread, called the producer hereafter, has to read lines from the standard input and to push them in a queue. The other threads, called the consumers hereafter, have to take these lines from the queue in the FIFO (first in first out) order and to print them.
We want to implement the queue with a linked-list. The first node of the list is the oldest enqueued line (first in), while the last node of the linked list is the newest enqueued line (last in). We also want to avoid following the whole linked-list to enqueue a new node. For this reason, you have to keep both a pointer to the first and to the last node.