Definition Threads in a process are like processes with shared memory. They have their own registers, program counter, and stack. explicit statements should be made if any memory is specific to a thread.

Unlike processes, who execute in their own memory space by default, threads allow multiple execution in the same memory space. This means they share code, data, file descriptors e.t.c. They are also relatively light-weight and cheap because of memory sharing.

Threads are used for concurrent programming such as network requests handling.


ThreadVSProcess


Functions In this article, Unix threads will be used for examples.

int pthread_create(pthread_t* thread,  
const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
 
/* 
thread: creates a handle to a thread at the pointer location,
attr: thread attributes,
start_routine: the function to execute,
arg: value to ba passed into start_routine
*/

pthread_create returns 0 on success, other code on error. An example would be

#include <pthread.h>
#include <stdio.h>
 
void* run(void*) { 
	printf("In run\n");
	return NULL; 
}
 
int main() {  
	pthread_t thread;  
	pthread_create(&thread, NULL, &run, NULL); printf("In main\n");
	return 0;
}
 
/* When a thread is created, it will run automatically with the function 'run'
*/

To make sure a thread waits until another thread finishes their execution, we would use pthread_join

int pthread_join(pthread_t thread, void** retval);
 
/*
the calling thread wait for this thread to terminate (thread must be joinable) retval stores exit status of thread (set by pthread_exit) to the loca- tion pointed by *retval. If cancelled returns PTHREAD_CANCELED. NULL is ignored.
*/

pthread_join returns 0 on success, other code on error. Please note: only call this once on a thread per thread. Multiple calls on the same thread lead to undefined behaviour.

pthread_exit is a great tool if we would like to exit early in a thread.

// retval return value passed to function that calls pthread_join
// returning from a start_routine is equivalent of calling pthread_exit.
// pthread_exit is called implicitly when the start_routine of a thread returns

Another use case of pthread_join is to clean up resources of a thread on exit. It is also possible to let a thread clean up its resources automatically on exit by making it detachable.

int pthread_detach(pthread_t thread);
 
// thread will be marked as detached through this function

pthread_detach returns 0 on success, error number otherwise. Please note that calling pthread_detach on a detached thread leads to undefined behaviour.

Also, detached threads are not joinable which means the main thread are not going to wait on them before returning.

To wait on all sub-threads when exiting, a main thread would have to call pthread_exit in the end to wait on all threads (whether joinable or detached).

#include <pthread.h>
#include <stdio.h>
 
void* run(void*) { 
	printf("In run\n");
	return NULL; 
}
 
int main() {  
	pthread_t thread;  
	pthread_create(&thread, NULL, &run, NULL); pthread_detach(thread);  
	printf("In main\n");  
	pthread_exit(NULL);
}

You may also set attributes for a thread. An example is:

size_t stacksize;  
pthread_attr_t attributes; pthread_attr_init(&attributes); pthread_attr_getstacksize(&attributes, &stacksize); printf("Stack size = %i\n", stacksize); pthread_attr_destroy(&attributes);

set a thread as joinable

pthread_attr_setdetachstate(&attributes,
                            PTHREAD_CREATE_JOINABLE);

We prefer threads over processes for following reasons:

  1. Threads are light-weight
  2. Threads have lower overheads for creation and context switching
  3. Threads share memory by default