How to wait for a thread to start to start initialization code

I am having a problem synchronizing the main thread with a recently launched child thread.

What I want to do:

  • the main thread creates a new child thread and blocks
  • the child thread starts and initializes (it may take some time)
  • After initializing the child thread, the main thread continues (and the two threads execute in parallel)

My first attempt was something like this:

typedef struct threaddata_ { int running; } threaddata_t; void*child_thread(void*arg) { threaddata_t*x=(threaddata_t)arg; /* ... INITIALIZE ... */ x->running=1; /* signal that we are running */ /* CHILD THREAD BODY */ return 0; } void start_thread(void) { threaddata_t*x=(threaddata_t*)malloc(sizeof(threaddata_t)); x->running=0; int result=pthread_create(&threadid, 0, child_thread, &running); while(!x->running) usleep(100); /* wait till child is initialized */ /* MAIN THREAD BODY */ } 

Now I didn’t like it at all, because it makes the main stream sleep, possibly for a longer period than necessary. So I made a second attempt using the mutual access conditions and conditions

  typedef struct threaddata_ { pthread_mutex_t x_mutex; pthread_cond_t x_cond; } threaddata_t; void*child_thread(void*arg) { threaddata_t*x=(threaddata_t)arg; /* ... INITIALIZE ... */ pthread_cond_signal(&x->x_cond); /* signal that we are running */ /* CHILD THREAD BODY */ return 0; } void start_thread(void) { threaddata_t*x=(threaddata_t*)malloc(sizeof(threaddata_t)); pthread_mutex_init(&x->x_mutex, 0); pthread_cond_init (&x->x_cond , 0); pthread_mutex_lock(&x->x_mutex); int result=pthread_create(&threadid, 0, child_thread, &running); if(!result)pthread_cond_wait(&x->x_cond, &x->x_mutex); pthread_mutex_unlock(&x->x_mutex); /* MAIN THREAD BODY */ } 

This seemed more reasonable than the first attempt (using the right signals, rather than rolling my own wait loop) until I found that this included the race condition: If the child thread completed initialization fast enough (before the main thread waits for the condition) , it will block the main thread.

I suppose my case is not so unusual, so there should be a very simple solution, but I do not see it right now.

+4
source share
3 answers

The correct way to use the condvar / mutex pair:

 bool initialised = false; mutex mt; convar cv; void *thread_proc(void *) { ... mt.lock(); initialised = true; cv.signal(); mt.unlock(); } int main() { ... mt.lock(); while(!initialised) cv.wait(mt); mt.unlock(); } 

This algorithm avoids any possible races. You can use any complex condition that was changed when the mutex was locked (instead of a simple initialized one).

+7
source

The correct tool for this is sem_t . The main thread initializes them with 0 and wait until it receives a token from a recently launched thread.

By the way, your mutex / cond solution has a race condition because the child thread does not block the mutex.

+2
source

The barrier should do the trick beautifully. Since you mention Win32 support in the comments, the latest versions of Win32 support barriers, so you don’t have to write your own shell to get some portability between Win32 and * nix.

Sort of:

 typedef struct threaddata_ { pthread_barrier_t* pbarrier; } threaddata_t; void* child_thread(void*arg) { threaddata_t*x=(threaddata_t*)arg; /* ... INITIALIZE ... */ int result = pthread_barrier_wait(x->pbarrier); /* CHILD THREAD BODY */ return 0; } void start_thread(void) { pthread_barrier_t barrier; int result = pthread_barrier_init(&barrier, NULL, 2); threaddata_t*x=(threaddata_t*)malloc(sizeof(threaddata_t)); x->pbarrier = &barrier; int result=pthread_create(&threadid, 0, child_thread, &x); result = pthread_barrier_wait(&barrier); /* child has reached the barrier */ pthread_barrier_destroy(&barrier); /* note: the child thread should not use */ /* the barrier pointer after it returns from*/ /* pthread_barrier_wait() */ /* MAIN THREAD BODY */ } 

The disadvantage of this solution is that it can, without the need, instantly block the child thread. If this is a problem, the way to solve the variable mentioned by Dmitry Porokh is the way to go.

+2
source

All Articles