POSIX Thread Programming (pthread)

TUTORIAL


1 Why we need threads and what is threads?

Basically we want to do two things at one time. For Garcia robots, we hope we could updating robots status (battery, ranger......) and acquiring wireless sensor mote readings at the same time. Also,  we want to share data between two jobs. Threads are functions that running concurrently. 



2 Example 1: Creating Threads. (thread1.c)

#include <stdio.h>
#include <stdlib.h>
// Including pthread API
#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.
}

Two while() loop is running concurrently.    They are also sharing data with the global variable i. One thread is printing i every 1 second,  the other process (main() ) is changing the value of i every 2 seconds.

The function pthread_create() creates a thread to run the function, and returns while the function is still running. So right NOW, you have two things running concurrently:

The main() function which keeps increasing i

The print_i() function which keeps printing i

And you should get a output like:
1
2
2
3
3
4
4
5
5


pthread_t t1;
 A struct to keep all the information of a thread,  it should be created in your program and pass in to the pthread_create() function.

pthread_create();    This function takes 4 arguments, the first argument is a pointer to a pthread_t structure; the second one is always NULL in our case; the third one is the function name of the function to be executed concurrently as a thread; the fourth is the parameter passed to the function (in this case print_i() ).  


3 Understanding pthread and threaded Programmings.

3.1 Unpredictivity of threads.

Consider this:

#include <stdio.h>
#include <stdlib.h>
// Including pthread API
#include <pthread.h>

// This function will run concurrently.
void* print_i(void *ptr) {
    printf("a\n");
    printf("b\n");
 
}

int main() {
  pthread_t t1;
  i = 1;
  int iret1 = pthread_create(&t1, NULL, print_i, NULL);
  printf("c\n");
}

What will the result be? There is no way you could tell, and you should not assume that they will always be the same order.  This is known as the unpredictable property of threaded programming.

****YOU HAVE TO assume that the two threads are running concurrently at RANDOM processing speed in any occasions***************

3.2  Library Thread Safe.

You have to use library functions, from the C standard function, to the Garcia API.  It is perfectly safe to call these functions in your program without concurrent programming, *BUT* somehow some functions will have trouble while you calling them concurrently.

A library function, which is ok to be called in multi threads, is often called a thread-safe function. Most Linux C standard functions are thread-safe. Most operations in GTK+ is also thread-safe. But in my API, functions are not.  

Trying to avoid calling the same  function of a thread-unsafe API at the same time from different threads.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Including pthread API
#include <pthread.h>

char s1[] = "abcdefg";
char s2[] = "abc";

char* c;
void last_letter(char* a, int i) {
  sleep(i);
  c = NULL;
  sleep(i);
  c = a;
  sleep(i); // or doing some dirty heavy work
  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);
  exit(0); //never reached.
}
Why some functions are threaded unsafe.

In this example, we see in function aa(), the value of c is changed from s2 to NULL at the 5th second. This is not a thread-safe function.



3 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().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Including pthread API
#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.
}
See output of this program with or without mutex.

With mutex
I am
in i
I am
in j
Without mutex (commenting two lines with underlines).
I am
I am
int i
in j


Using Mutex smartly (sending signals between processes).



Mutex will easily become the origin of chaos.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Including pthread API
#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.
}

Deadlock situation.



Useful resources:

http://students.cs.byu.edu/~cs460ta/cs460/labs/pthreads.html

http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html