CS 23 Software Design and Implementation
Lecture 22
POSIX Thread Programming
In the this lecture we study thread programming.
What is a thread? Well we have studied the forking of process to support concurrency. Thread are units of
control that execute within the context of a single process representing multiple strands of indepenent
execution.
What is the difference between forking processes and threads. Well typically when a process is forked it
executes as new independent process with its own PID and a copy of the code and resources of the parent
process. It is scheduled by the OS as a independent process. A process has a single thread by default.
Threads running in a process get their own stack and run concurrently and share global variables in the
process.
We will need these skills for the robotics projects.
In this lecture we will just look at a number of simple examples of code to illustrate how threads are
created and how we can implement mutual exclusion using mute for shared resouces. These notes are not
meant to be exhaustive - they are not.
Goals
We plan to learn the following from today’s lecture:
- Creating a thread
- Random behavior
- Safe and unsafe threads
- Mutex
- Deadlock issues
Thread Creation
The code creates a thread; that means two threads are running - the main thread and the print˙i
thread.
// File print_1.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// global variable to share data
int i;
// This function will run concurrently.
void* print_i(void *ptr) {
while (1) {
sleep(1);
printf("%d\n", i);
}
}
int main() {
pthread_t t1;
i = 1;
int iret1 = pthread_create(&t1, NULL, print_i, NULL);
while (1) {
sleep(2);
i = i + 1;
}
exit(0); //never reached.
}
Output from running the code
atc@Macintosh-25 l22] ./thread
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
etc.
Unpredicatability
What get output first? We do not know what order the values will be writtem out. If the main tread
completes beore the print˙i thread has executed them it will die.
// File: random.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
// This function will run concurrently.
void* print_i(void *ptr) {
printf("a\n");
printf("b\n");
}
int main() {
pthread_t t1;
int i = 1;
int iret1 = pthread_create(&t1, NULL, print_i, NULL);
printf("c\n");
}
Output from running the code
atc@Macintosh-25 l22] ./thread
c
Functions threaded unsafe
Typically libraries are thread safe. However many functions are said to be thread unsafe. As
below.
// File: unsafe threads
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
char s1[] = "abcdefg";
char s2[] = "abc";
char* c;
void last_letter(char* a, int i) {
printf("last letter a is %s and i is %d\n", a, i);
sleep(i);
c = NULL;
sleep(i);
c = a;
sleep(i);
while (*(c)) {
c++;
}
printf("%c\n", *(c-1));
}
// This function will run concurrently.
void* aa(void *ptr) {
last_letter(s2, 2);
}
int main() {
pthread_t t1;
int iret1 = pthread_create(&t1, NULL, aa, NULL);
last_letter(s1, 5);
sleep(10);
exit(0); //never reached.
}
Output from running the code
[atc@Macintosh-25 l22] gcc -o thread unsafe.c
[atc@Macintosh-25 l22] ./thread
last letter a is abcdefg and i is 5
last letter a is abc and i is 2
Bus error
Mutex lock
Mutex is a special mechanism to help creating concurrency programs. Treat Mutex as a lock, there are two
functions. pthread˙mutex˙lock(mutex) AND pthread˙mutex˙unlock(mutex).
pthread˙mutex˙lock(mutex): If the mutex is unlocked, this function will lock the mutex until
pthread˙mutex˙unlock(mutex) is called, and returns; otherwise, it will block until the lock is
unlocked;
pthread˙mutex˙unlock(mutex): If the mutex is locked by some pthread˙mutex˙lock() function, this function
will unlock the mutex; otherwise it does nothing.
See the following programs, what we want to do in this program is to ensure that at the same time there is
only one thread calling function print().
Typically libraries are thread safe. However many functions are said to be thread unsafe. As
below.
// File: mutex.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
char s1[] = "abcdefg";
char s2[] = "abc";
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
void print(char* a, char* b) {
pthread_mutex_lock(&mutex1);
printf("1: %s\n", a);
sleep(1);
printf("2: %s\n", b);
pthread_mutex_unlock(&mutex1);
}
// These two functions will run concurrently.
void* print_i(void *ptr) {
print("I am", " in i");
}
void* print_j(void *ptr) {
print("I am", " in j");
}
int main() {
pthread_t t1, t2;
int iret1 = pthread_create(&t1, NULL, print_i, NULL);
int iret2 = pthread_create(&t2, NULL, print_j, NULL);
while(1){}
exit(0); //never reached.
}
Output from running the code
[atc@Macintosh-25 l22] ./thread
1: I am
2: in i
1: I am
2: in j
// With the mutex commented out the output is:
[atc@Macintosh-25 l22] ./thread
1: I am
1: I am
2: in i
2: in j
Deadlock
Deadlock is a classic problem that you can fall into with mutex threads.
// File: deadlock.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
// These two functions will run concurrently.
void* print_i(void *ptr) {
pthread_mutex_lock(&mutex1);
pthread_mutex_lock(&mutex2);
printf("I am in i");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
}
void* print_j(void *ptr) {
pthread_mutex_lock(&mutex2);
pthread_mutex_lock(&mutex1);
printf("I am in j");
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
}
int main() {
pthread_t t1, t2;
int iret1 = pthread_create(&t1, NULL, print_i, NULL);
int iret2 = pthread_create(&t2, NULL, print_j, NULL);
while(1){}
exit(0); //never reached.
}
Output from running the code
[atc@Macintosh-25 l22] ./thread