POSIX Threads – Threading with C : Part 1
General
A Thread generally, is a set of instructions, that can be scheduled to run by the OS. Threads can be considered as process children, since they are created from inside a process, but a thread runs independent from the parent process. The only way they can affect each other is when a process is killed, so all child threads are killed too.
On single-processor or single-core systems, as you can imagine, two sets of instructions cannot be executed in the same time, so the processor switches between them all the time, and in this way, making it look like they happen simultaneously.
On the other hand, multi-processor or multi-core systems, can really execute many sets of instructions in the same time, by assigning each processor / core a set.
Threads vs Processes
Processes are independent, while a thread’s execution, depends weather it’s parent is alive.
Processes use their own memory and carry many state information, while threads share their state and memory, thus making them easy for the OS to initialize.
Communication between threads is easier and faster than processes.
A big problem
Consider a process with two child threads. As we said, threads share the same memory. So, what happens when the two threads try to write on the same memory block? That is the main problem of parallel-computing. You might say that the odds for this to happen are way too low, but in fact, this is one of the biggest problems of big applications, which use threads to make use of multi-processor / core systems. Lately there are many ideas developed in order to bypass this problem, but that’s out of the scope of this post. Check links during the post for more information.
For the developer
Okay, since we are aware of the general idea how threads work, lets try to find out how our applications can take advantage of threading.
There are many threading standards / techniques, but we are going to stick with the most used. POSIX Threads, or pthreads. POSIX stands for “Portable Operating System Interface for Unix” and is a set of standards define by the IEEE, about API’s for Unix. pthreads are commonly used on Unix or Unix-like OS’es, but there are various implementations for Windows too, like pthreads-w32.
pthreads, defines a C library, usable with the
[c]
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
//callback function.
void hello() {
printf("Im a thread!\n");
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
pthread_t threads[NUM_THREADS]; //Declaring 5 threads.
int rc, t;
//Cycling through threads[5] to pthread_create() each thread.
for(t=0; t < NUM_THREADS; t++) {
printf("Initializing thread %d\n", t);
rc = pthread_create(&threads[t], NULL, (void *)hello, NULL); //creating each thread.
if (rc != 0){ //if pthread_create() doesn’t return 0, then we have error.
printf("ERROR; return code from pthread_create() is %d\n", rc);
return -1;
}
}
pthread_exit(NULL);
return 0;
}
[/c]
Analyzing
Let’t examine the new functions..
[c light="true"]int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void*), void *arg);[/c]
- pthread_t *thread, a pointer to a pthread.
- const pthread_attr_t *attr – Attributes concerning the thread, set NULL for default attributes.
- void *( *start_routine)(void *) – Pointer to (void *)function that will be called by the thread.
- void *arg – Pointer to argument to provide to the callback function.
[c light="true"]void pthread_exit(void *retval);[/c]
This function terminates the thread with return value of void *retval.
Compiling
In order to compile a C source code using <pthread.h> we need to include the flag -pthread to our compiler, so the compile command should look like this :
[c light="true"]gcc -pthread -o helloworld_thread helloworld_thread.c[/c]
Passing Arguments
The pthread_create() function doesnt allow us to pass more than one argument to the callback function, so if we need to pass more, we have to wrap them inside a struct. Let’s see an example :
[c]
#include <pthread.h>
#include <stdio.h>
typedef struct FOO {
int i;
int y;
} foo;
void hello(void *arg) {
foo bar = *(foo*)(arg);
printf( "This is coming from a thread:\n");
printf("arg.i = %d\narg.y = %d\n", bar.i, bar.y);
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
foo bar;
bar.i = 5;
bar.y = 6;
pthread_t mythread;
int rc, t;
printf("Initializing thread!\n");
rc = pthread_create(&mythread, NULL, (void *)hello, (void *)&bar);
if (rc != 0){
printf("ERROR; return code from pthread_create() is %d\n", rc);
return -1;
}
pthread_exit(NULL);
return 0;
}
[/c]
Passing only one argument is easy, you just cast it to (void *) before the call, and then cast it back to it's normal type from inside the callback function. But since i want to pass more arguments, i pass the struct as argument.
Resources : [ computing.llnl.gov, yolinux.com ]
Incoming Part 2 : Advanced thread parameters, joining / detaching / killing threads.
As always, you can subscribe via RSS or follow wedevblog on twitter for the latest development posts!
#include <pthread.h> #include <stdio.h> #define NUM_THREADS 5 void *PrintHello(void *threadid) { long tid; tid = (long)threadid; printf("Hello World! It's me, thread #%ld!\n", tid); pthread_exit(NULL); } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; int rc; long t; for(t=0; t<NUM_THREADS; t++){ printf("In main: creating thread %ld\n", t); rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } pthread_exit(NULL); }


Recent Comments