For some reason I thought that calling pthread_exit(NULL)
at the end of a main function would guarantee that all running threads (at least created in the main function) would finish running before main
could exit. However when I run this code below without calling the two pthread_join
functions (at the end of main
) explicitly I get a segmentation fault, which seems to happen because the main
function has been exited before the two threads finish their job, and therefore the char buffer is not available anymore. However when I include these two pthread_join
function calls at the end of main
it runs as it should. To guarantee that main
will not exit before all running threads have finished, is it necessary to call pthread_join
explicitly for all threads initialized directly in main
?
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <semaphore.h>
#define NUM_CHAR 1024
#define BUFFER_SIZE 8
typedef struct {
pthread_mutex_t mutex;
sem_t full;
sem_t empty;
char* buffer;
} Context;
void *Reader(void* arg) {
Context* context = (Context*) arg;
for (int i = 0; i < NUM_CHAR; ++i) {
sem_wait(&context->full);
pthread_mutex_lock(&(context->mutex));
char c = context->buffer[i % BUFFER_SIZE];
pthread_mutex_unlock(&(context->mutex));
sem_post(&context->empty);
printf("%c", c);
}
printf("\n");
return NULL;
}
void *Writer(void* arg) {
Context* context = (Context*) arg;
for (int i = 0; i < NUM_CHAR; ++i) {
sem_wait(&context->empty);
pthread_mutex_lock(&(context->mutex));
context->buffer[i % BUFFER_SIZE] = 'a' + (rand() % 26);
float ranFloat = (float) rand() / RAND_MAX;
if (ranFloat < 0.5) sleep(0.2);
pthread_mutex_unlock(&(context->mutex));
sem_post(&context->full);
}
return NULL;
}
int main() {
char buffer[BUFFER_SIZE];
pthread_t reader, writer;
Context context;
srand(time(NULL));
int status = 0;
status = pthread_mutex_init(&context.mutex, NULL);
status = sem_init(&context.full,0,0);
status = sem_init(&context.empty,0, BUFFER_SIZE);
context.buffer = buffer;
status = pthread_create(&reader, NULL, Reader, &context);
status = pthread_create(&writer, NULL, Writer, &context);
pthread_join(reader,NULL); // This line seems to be necessary
pthread_join(writer,NULL); // This line seems to be necessary
pthread_exit(NULL);
return 0;
}
If that is the case, how could I handle the case where plenty of identical threads (like in the code below) would be created using the same thread identifier? In that case, how can I make sure that all the threads will have finished before main
exits? Do I really have to keep an array of NUM_STUDENTS pthread_t
identifiers to be able to do this? I guess I could do this by letting the Student threads signal a semaphore and then let the main
function wait on that semaphore, but is there really no easier way to do this?
int main()
{
pthread_t thread;
for (int i = 0; i < NUM_STUDENTS; i++)
pthread_create(&thread,NULL,Student,NULL); // Threads
// Make sure that all student threads have finished
exit(0);
}
Quite aside from whether the program should or should not terminate when the main thread calls
pthread_exit
,pthread_exit
saysAnd also:
Since the context is an automatic variable of
main()
, your code can fall over before it even gets to the point of testing what you want it to test...There is no need for calling
pthread_join(reader,NULL);
at all ifContext
andbuffer
are declared with static storage duration (as already pointed out by Steve Jessop, caf and David Schwartz).Declaring
Context
andbuffer
static also makes it necessary to changeContext *context
toContext *contextr
orContext *contextw
respectively.In addition, the following rewrite called
pthread_exit.c
replacessem_init()
withsem_open()
and usesnanosleep()
(as suggested by Jonathan Leffler).pthread_exit
was tested on Mac OS X 10.6.8 and did not output any ASCII NUL characters.pthread_exit(3)
exits the thread that calls it (but not the whole process if other threads are still running). In your example other threads use variables onmain
's stack, thus whenmain
's thread exits and its stack is destroyed they access unmapped memory, thus the segfault.Use proper
pthread_join(3)
technique as suggested by others, or move shared variables into static storage.pthread_exit()
is a function called by a thread to terminate its own execution. For the situation you've given it is not to be called from your main program thread.As you have figured out,
pthread_join()
is the correct means to wait for the completion of a joinable thread frommain()
.Also as you've figured out, you need to maintain the value returned from
pthread_create()
to pass topthread_join()
.What this means is that you cannot use the same
pthread_t
variable for all the threads you create if you intend to usepthread_join()
.Rather, build an array of
pthread_t
so that you have a copy of each thread's ID.pthread_join()
is the standard way to wait for the other thread to complete, I would stick to that.Alternatively, you can create a thread counter and have all child threads increment it by 1 at start, then decrement it by 1 when they finish (with proper locking of course), then have your
main()
wait for this counter to hit 0. (pthread_cond_wait()
would be my choice).When you pass a thread a pointer to a variable, you need to ensure that the lifetime of that variable is at least as long as the thread will attempt to access that variable. You pass the threads pointers to
buffer
andcontext
, which are allocated on the stack insidemain
. As soon asmain
exits, those variables cease to exist. So you cannot exit frommain
until you confirm that those threads no longer need access to those pointers.95% of the time, the fix for this problem is to follow this simple pattern:
Sadly, this doesn't work well for objects shared by two or more threads. In that case, you can put a use count and a mutex inside the parameter object. Each thread can decrement the use count under protection of the mutex when it's done. The thread that drops the use count to zero frees the object.
You would need to do this for both
buffer
andcontext
. Set the use count to2
and then pass a pointer to this object to both threads.