Week 11 Tutorial Questions
Objectives
- introduce the concept of multithreading, concurrency, and parallelism in processes
- introduce the range of concurrency problems that can occur in multithreaded programs
- introduce tools to manage concurrency problems in multithreaded programs
Code Review
This week the code review is for file_modes.c. The reviewees should give a brief description of their code, and the class should ask questions, comment on the quality of the code, and suggest improvements. Each review should take about 10 minutes.
Code reviews should take place in the second hour of tutorials for the week
Tutorial Questions
-
What is the difference between concurrency and parallelism?
-
How can we make our programs concurrent in C?
-
What are the benefits and disadvantages of executing processes concurrently?
-
The following C program attempts to say hello from another thread:
#include <stdio.h> #include <pthread.h> void *thread_run(void *data) { printf("Hello from thread!\n"); return NULL; } int main(void) { pthread_t thread; pthread_create( &thread, // the pthread_t handle that will represent this thread NULL, // thread-attributes -- we usually just leave this NULL thread_run, // the function that the thread should start executing NULL // data we want to pass to the thread -- this will be // given in the `void *data` argument above ); return 0; }
However, when running this program after compiling with
gcc
, the thread doesn't say hello.gcc -pthread program.c -o program ./program ./program ./program
Why does our program exhibit such behaviour?
How can we fix it?
-
Write a C program that creates a thread that infinitely prints some message provided by main (eg.
"Hello\n"
), while the main (default) thread infinitely prints a different message (eg."there!\n"
). Consider the following code
#include <pthread.h> // for pthread_create, pthread_join, pthread_exit #include <stdio.h> #include <stdlib.h> #define N 4 void * helloThread(void * arg){ int i = * (int *) arg; printf("Hello from Thread %d\n",i); return NULL; } int main(void){ pthread_t tid[N]; int i; for(i=0; i < N; i++){ if( pthread_create(&tid[i], NULL, helloThread, &i) != 0){ fprintf(stderr, "\n ERROR creating thread %d",i); exit(1); }; } for(i=0; i< N; i++){ if (pthread_join(tid[i], NULL) != 0){ fprintf(stderr, "\n ERROR joining thread"); exit(1); } } exit(0); }
Explain how I could possibly get the following output.
./program Hello from Thread 1 Hello from Thread 2 Hello from Thread 1 Hello from Thread 3 ./program Hello from Thread 1 Hello from Thread 2 Hello from Thread 3 Hello from Thread 2
Fix it to make sure the following lines are printed out every time the program is run. The lines can be printed out in any order
Hello from Thread 0 Hello from Thread 1 Hello from Thread 2 Hello from Thread 3
-
Concurrency can allow our programs to perform certain actions simultaneously that were previously tricky for us to do as CP1521 students.
For example, with our current C knowledge, we cannot execute any code while waiting for input (with, for example,
scanf
,fgets
, etc.).Write a C program that creates a thread which infinitely prints the message
"feed me input!\n"
once per second (sleep(3)), while the main (default) thread continuously reads in lines of input, and prints those lines back out tostdout
with the prefix:"you entered: "
. -
The following C program attempts to increment a global variable in two different threads, 5000 times each.
#include <stdio.h> #include <pthread.h> int global_total = 0; void *add_5000_to_counter(void *data) { for (int i = 0; i < 5000; i++) { // sleep for 1 nanosecond nanosleep (&(struct timespec){.tv_nsec = 1}, NULL); // increment the global total by 1 global_total++; } return NULL; } int main(void) { pthread_t thread1; pthread_create(&thread1, NULL, add_5000_to_counter, NULL); pthread_t thread2; pthread_create(&thread2, NULL, add_5000_to_counter, NULL); pthread_join(thread1, NULL); pthread_join(thread2, NULL); // if program works correctly, should print 10000 printf("Final total: %d\n", global_total); }
Since the global starts at 0, one may reasonably assume the value would total to 10000.
However, when running this program, it often gives differing values each individual execution:
dcc -pthread program.c -o program ./program Final total: 9930 ./program Final total: 9983 ./program Final total: 9994 ./program Final total: 9970 ./program Final total: 10000 ./program Final total: 9996 ./program Final total: 9964 ./program Final total: 9999
Why does our program exhibit such behaviour?
-
How can we use "mutual exclusion" to fix the previous program?
-
How can we use atomic types to fix the previous program?
-
Explain why running the following code can result in deadlock
#include <stdio.h> #include <pthread.h> #include <semaphore.h> int andrews_bank_account1 = 100; pthread_mutex_t bank_account1_lock = PTHREAD_MUTEX_INITIALIZER; int andrews_bank_account2 = 200; pthread_mutex_t bank_account2_lock = PTHREAD_MUTEX_INITIALIZER; // swap values between Andrew's two bank account 100,000 times void *swap1(void *argument) { for (int i = 0; i < 100000; i++) { pthread_mutex_lock(&bank_account1_lock); pthread_mutex_lock(&bank_account2_lock); int tmp = andrews_bank_account1; andrews_bank_account1 = andrews_bank_account2; andrews_bank_account2 = tmp; pthread_mutex_unlock(&bank_account2_lock); pthread_mutex_unlock(&bank_account1_lock); } return NULL; } // swap values between Andrew's two bank account 100,000 times void *swap2(void *argument) { for (int i = 0; i < 100000; i++) { pthread_mutex_lock(&bank_account2_lock); pthread_mutex_lock(&bank_account1_lock); int tmp = andrews_bank_account1; andrews_bank_account1 = andrews_bank_account2; andrews_bank_account2 = tmp; pthread_mutex_unlock(&bank_account1_lock); pthread_mutex_unlock(&bank_account2_lock); } return NULL; } int main(void) { //create two threads performing almost the same task pthread_t thread_id1; pthread_create(&thread_id1, NULL, swap1, NULL); pthread_t thread_id2; pthread_create(&thread_id2, NULL, swap2, NULL); // threads will possibly never finish // deadlock will likely occur pthread_join(thread_id1, NULL); pthread_join(thread_id2, NULL); return 0; }
- Write a program where a child process writes to a parent process. The child should send the following array of strings, one by one
char * messages[] = {"red\n", "yellow\n", "pink\n", "green\n", "purple\n", "orange\n", "blue\n"};
The parent should print out the strings it receives. - Write a program using popen that implements the following using popen :
grep main file.txt | wc