4

我无法将主线程同步到最近启动的子线程。

我想做的是:

  • 主线程创建一个新的子线程并阻塞
  • 子线程启动并初始化(可能需要一些时间)
  • 一旦子线程被初始化,主线程继续(并且两个线程并行运行)

我的第一次尝试是这样的:

  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 */
  }

现在我根本不喜欢这个,因为它迫使主线程睡眠的时间可能比必要的更长。所以我做了第二次尝试,使用互斥锁和条件

  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 sane than the first attempt (using proper signals rather than rolling my own wait loop), until I discovered, that this includes a race condition: If the child thread has finished the initialization fast enough (before the main thread waits for the condition), it will deadlock the main thread.

I guess that my case is not so uncommon, so there must be a really easy solution, but I cannot see it right now.

4

3 回答 3

9

Proper way of condvar/mutex pair usage:

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 modified when mutex locked (instead of the simple !initialised).

于 2012-07-13T14:07:43.493 回答
2

The correct tool for that are sem_t. The main thread would initialize them with 0 and wait until it receives a token from the newly launched thread.

BTW your mutex/cond solution has a race condition because the child thread is not locking the mutex.

于 2012-07-13T14:01:49.853 回答
2

A barrier should do the trick nicely. Since you mention the need for support on Win32 in comments, barriers are supported on the latest Win32 pthreads, so you shouldn't have to write your own wrapper to get some portability between Win32 and *nix.

Something like:

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 drawback to this solution is that it may needlessly blocks the child thread momentarily. If that's a problem, the condition variable solution mentioned by Dmitry Poroh is the way to go.

于 2012-07-13T17:34:24.347 回答