POSIX thread programming
Introduction:
Using thread has many benefits. They share the same resources of their calling process (parent process), like global variables, open files and so on. Also they have their own thread ID, stacks, and so on. For more information, please refer to the old article "Multitasking in Embedded Linux" and reference [1]. Here we focus on how to use it.
The simplest thread programming:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *print_message(void *ptr)
{
char *text;
text=(char *)ptr;
printf("%s \n", text);
}
main()
{
pthread_t thread1, thread2;
char *str1="I am thread1";
char *str2="I am thread2";
int T1ret, T2ret;
/*Create two threads*/
T1ret=pthread_create(&thread1, NULL, print_message, (void*) str1);
T2ret=pthread_create(&thread2, NULL, print_message, (void*) str2);
/*Wait for both threads to finish*/
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("T1 & T2 return: %d, %d\n", T1ret, T2ret);
}
#include <stdlib.h>
#include <pthread.h>
void *print_message(void *ptr)
{
char *text;
text=(char *)ptr;
printf("%s \n", text);
}
main()
{
pthread_t thread1, thread2;
char *str1="I am thread1";
char *str2="I am thread2";
int T1ret, T2ret;
/*Create two threads*/
T1ret=pthread_create(&thread1, NULL, print_message, (void*) str1);
T2ret=pthread_create(&thread2, NULL, print_message, (void*) str2);
/*Wait for both threads to finish*/
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("T1 & T2 return: %d, %d\n", T1ret, T2ret);
}
Here we use pthread_create function to create a thread. There are four arguments in it.
First argument: used to return the thread ID.
Second argument: used to set the attributes of thread, like priority, schedule policy and so on. For detail, please refer to reference [1].
Third argument: assign the thread function which is the name of the function.
Four argument: used to pass value.
Usually at the end of the calling function, we use pthread_join(threadid, NULL) to wait for termination of that thread. This function is very important, because sometimes we need to allocate some memories for threads (refer to reference [2]). After all the threads terminates, we need to free these memories.
How to pass value to thread:
We can only use the fourth argument of pthread_create() to pass value. And it is declared as void *, so it is a totally free local variable. It can be converted to any type of variables by using casting.
Passing of pointer:
This is easy, because the fourth argument is a void pointer itself. So you can use the pointer as fourth argument. In thread, you need to cast it to the type you like.
Passing of value:
Two ways to do this.
1. In thread_create() function, cast the variable to be passed to void * type. Then in thread, you can cast it to any type you want. For example:
rc=pthread_create(&threads[t], NULL, printhello. (void *)t);
In thread:
void *printhello(void *threadid)
{
long taskid=(long)threadid;
...
}
2. Allocate a or several memories using malloc( ) function (refer to [2]) which return a pointer points to that location. Then you can use it like using pointer we talked in part 1. Here must remember to free the location using free( ) at the end of the function who called malloc( ).
long *taskids[NUM_THREADS];
for(t=0; t<NUM_THREADS; t++)
{
taskids[t] = (long *) malloc(sizeof(long));
*taskids[t] = t;
printf("Creating thread %ld\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
...
}
Passing of multiple parameters:
Use structure (struct) to this. Imagine yourself.
struct thread_data{
int thread_id;
int sum;
char *message;
};
struct thread_data thread_data_array[NUM_THREADS];
void *PrintHello(void *threadarg)
{
struct thread_data *my_data;
...
my_data = (struct thread_data *) threadarg;
taskid = my_data->thread_id;
sum = my_data->sum;
hello_msg = my_data->message;
...
}
int main (int argc, char *argv[])
{
...
thread_data_array[t].thread_id = t;
thread_data_array[t].sum = sum;
thread_data_array[t].message = messages[t];
rc = pthread_create(&threads[t], NULL, PrintHello,
(void *) &thread_data_array[t]);
...
}
Synchronization using Mutex:
Initialization of mutex:
pthread_mutex_t mutex1=PTHREAD_MUTEX_INITIALIZER;
Lock a mutex:
pthread_mutex_lock(&mutex1);
Unlock a mutex:
pthread_mutex_unlock(&mutex1);
Destroy a mutex:
pthread_mutex_destroy(&mutex1);
Set the stack size of thread:
pthread_attr_t attr; // Here we define a attribute variable.
size_t mystacksize;
pthread_attr_getstacksize(&attr, &mystacksize); // Here we get the stack size of variable attr. Remember it
// belongs to variable attr, not the thread who calling it.
// belongs to variable attr, not the thread who calling it.
pthread_attr_setstacksize(&attr, mystacksize); // Here we set the stack size of variable attr. Also remember
// it belongs to variable attr.
// it belongs to variable attr.
Remember to put variable attr into the second argument of function pthread_create( ), like:
pthread_create(&threads[t], &attr, dowork, (void *) t);
At last, remember to destroy attr:
pthread_attr_destroy(&attr);
This procedure also can be applied to other attributes setting like joinable, scheduling, priority and so on.
References:
[1] Linux Tutorial: POSIX Thread, http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html
[2] malloc - Linux man page, http://linux.die.net/man/3/malloc
[3] thread in Linux using C/C++, http://forum.codecall.net/c-tutorials/39755-threads-linux-using-c-c-part-1-a.html
[4] POSIX Threads Programming, https://computing.llnl.gov/tutorials/pthreads/#Abstract
[5] Is it legitimate to pass an argument as void*?, http://stackoverflow.com/questions/2972846/is-it-legitimate-to-pass-an-argument-as-void
[6] pthread_attr_destroy( ) -- Destroy a thread attributes object, http://cursuri.cs.pub.ro/~apc/2003/resources/pthreads/uguide/users-g5.htm
Comments
Post a Comment