My question is related to thread programming in C.
My problem is that I just want to create two threads in my main
program. These two threads should work sequentially, which means my first thread should execute first (no other statement of any thread should be executed). First thread should have complete control. No other statement of any other thread, even main
program statements, should be executed until the first thread is complete.
After completion of the first thread, the second thread should be executed in a similar way as the first.
After that my main should execute.
I know you can say why the hell I want to do this, because this thing can be achieved by just creating two functions and calling them in sequence, but for learning and for experimentation I want to do it with the help of threads.
I write some code in C as follows:
void* fun()
{
printf("\nThe thread 1 is running");
}
void* van()
{
printf("\nthread 2 is running ");
}
int main()
{
pthread_t t1,t2;
pthread_create(&t1,NULL,fun,NULL);
pthread_create(&t2,NULL,van,NULL);
printf("\nI'm in main\n");
pthread_join(t2,NULL);
}
The program is working perfectly but I don't understanding the working of the function pthread_join()
.
When I change my code a little bit as follows:
int main()
{
pthread_t t1,t2;
pthread_create(&t1,NULL,fun,NULL);
pthread_join(t2,NULL); // Change
pthread_create(&t2,NULL,van,NULL);
printf("\nI'm in main\n");
}
Now when I run the code it shows a segmentation fault.
Now my questions are the following:
- What is the attribute parameter in the
pthread_create()
function? Why do we use them? What are the default attributes for a thread? Please explain with an example. - What is the argument in
pthread_create()
function? Why we use them? What are the default arguments for a thread? Please explain with an example. - How does
pthread_join()
work actually? What does it mean when my code callspthread_join()
in main witht2
as first argument. Does it mean main should suspend its execution until t2 execution has completed or something else? - What is the second argument in
pthread_join()
? Why do we use it? What is its default value? Please explain with an example or code.
4 Answers 4
The attr argument points to a pthread_attr_t structure whose contents are used at thread creation time to determine attributes for the new thread; this structure is initialized using pthread_attr_init(3) and related functions. If attr is NULL, then the thread is created with default attributes (source).
Argument is passed to your thread function. This is the best way of passing data to the thread (as opposed to using global variables, e.g.).
Yes,
pthread_join
waits until the thread finishes. That's why your program fails when you callpthread_join
before you start the thread, since t2 contains junk at that point.If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e., the value that the target thread supplied to pthread_exit(3)) into the location pointed to by *retval. If the target thread was canceled, then PTHREAD_CANCELED is placed in *retval. (source).
I.e., you can make your thread function to notify you about its execution result.
Given this, your thread creation may look like this:
struct th_arg{
...
};
static void th_work(struct th_arg* a){
//...some work
if (success) pthread_exit(EXIT_SUCCESS)
else pthread_exit(ERROR_CODE);
}
int main(){
int t1,t2;
struct th_arg[2];
int codes[2];
// initialize th_arg2
pthread_create(&t1, NULL, th_work, th_arg+0, th_arg+0);
pthread_create(&t2, NULL, th_work, th_arg+1, th_arg+1);
pthread_join(t1, codes+0);
pthread_join(t2, codes+1);
if (codes[0] == EXIT_SUCCESS && codes[1] == EXIT_SUCCESS){
...
}
}
Best source to learn posix threads is https://computing.llnl.gov/tutorials/pthreads/
The attribute in
pthread_create
is of typepthread_attr_t
. It holds information about the thread, including things like where the thread's memory address starts and how large its stack can get. Most of the time you won't need to use this parameter.The arg in
pthread_create
is passed to thestart_routine
function as the one and only argument. This is kind of a hack. Since a pointer can point at anything (an int, an array, a struct, etc), you can pass in your arguments by passing a pointer to ALL of the data you want to pass in. Most of the time people end up going with a struct. There are no default arguments for a thread. If you don't want to pass anything in, just specifyarg
as NULL.
As an example:
struct arguments
{
int something;
char* anotherArg;
short stillAnotherArg;
}
void* fun(void* arg)
{
struct arguments *my_args = (struct arguments*)arg;
printf("You said: %s\n", my_args->anotherArg); // Will print "You said: Hello there"
...
}
int main()
{
pthread_t t1;
struct arguments my_args;
my_args.something = 5;
my_args.anotherArg = "Hello there";
my_args.stillAnotherArg = 42;
pthread_create (&t1,NULL,fun, &my_args);
...
}
pthread_join
blocks the caller until the specified thread has finished.The second argument to
pthread_join
allows you to capture the return value of the thread once it's done executing. Each of your threads returns avoid*
, and the second parameter allows you to specify where that return value gets stored.
As an example:
void* fun(void* arg)
{
...
return NULL; // not very exciting, but I don't want to get
// into memory management problems right now
}
int main()
{
...
void* theReturnValue;
pthread_join(t2, &theReturnValue);
printf("fun returned %p\n", theReturnValue); // will print "fun returned 0x0"
// which is NULL
}
Finally, the reason it segfaults is because you're waiting for a thread that hasn't been defined yet.
You can use omp library by including omp.h. It is easy to use. You can create a parallel section with the directive
#pragma omp parallel
{
// PARALLEL Code
}
And setting thread numbers with omp_set_num_threads(2);
and then get the id of a thread with omp_get_thread_num();
Example:
omp_set_num_threads(2);
#pragma omp parallel
{
printf("thread %d said hello", omp_get_thread_num());
}
main()
does not wait for thread 1 to complete before launching thread 2. If you waited for thread 1 to complete (pthread_join(&t1, NULL);
after itspthread_create()
, that would minimize the concurrent execution. Repeat for thread 2. Then you can run theprintf()
inmain()
itself. Since you've already waited for each thread to complete, you're done.