如何等待启动线程执行init代码

时间:2012-07-13 13:55:17

标签: c multithreading synchronization pthreads

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

我想做的是:

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

我的第一次尝试是:

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

这似乎比第一次尝试(使用适当的信号而不是滚动自己的等待循环)更加理智,直到我发现,这包括竞争条件:       如果子线程已经足够快地完成初始化(在主线程等待条件之前),它将使主线程死锁。

我想我的情况并非如此罕见,所以必须有一个非常简单的解决方案,但我现在看不到它。

3 个答案:

答案 0 :(得分:7)

condvar /互斥对使用的正确方法:

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();
}

此算法可避免任何可能的比赛。您可以使用在锁定互斥锁时修改的任何复杂条件(而不是简单的!初始化)。

答案 1 :(得分:2)

正确的工具是sem_tmain线程将使用0初始化它们,并等待它从新启动的线程接收到令牌。

BTW你的互斥锁/ cond解决方案有竞争条件,因为子线程没有锁定互斥锁。

答案 2 :(得分:2)

障碍应该很好地完成这个伎俩。由于您在评论中提到了对Win32的支持需求,因此最新的Win32 pthreads支持障碍,因此您不必编写自己的包装器以在Win32和* nix之间获得一些可移植性。

类似的东西:

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

这种解决方案的缺点是它可能会不必要地暂时阻塞子线程。如果这是一个问题,那么condition variable solution mentioned by Dmitry Poroh就可以了。