2012-07-13 108 views
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 */ 
    } 

现在我完全不喜欢这样,因为它强制主线程睡眠的时间可能超过必要的时间。 所以我做了第2次尝试,使用互斥&条件

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

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

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

回答

7

有道:

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

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

+0

+1:也用于防止虚假唤醒到'while()'循环。 – alk 2012-07-14 15:04:59

+0

请注意,这与OP的两种方法(第一种方法中的“usleep()”被pthread_cond_wait()替换)完全相同。 – caf 2012-07-15 13:08:15

+0

请注意,第一种方法中的运行标记不受互斥锁保护。 – 2012-07-15 14:11:45

2

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

顺便说一句你的互斥/ cond解决方案有一个竞争条件,因为子线程没有锁定互斥锁。 condvar /互斥对使用的

+0

我会研究互斥体,但在张贴后我也注意到了种族条件的原因。我是否正确理解,只需将pthread_cond_signal()的调用与我的互斥体的锁定/解锁(在调用pthread_create之前锁定)就足够了?我认为它应该并且不需要像@ dmitry-poroh – 2012-07-13 14:19:56

+0

提出的“while(!initialized)wait()”循环似乎sem_t在pthread的某些w32实现上不可用(至少实现我必须使用)。该代码应该在各种系统上运行,包括w32,这几乎排除了该解决方案,尽管它确实似乎是最优雅的一个。 – 2012-07-13 14:30:51

+0

是的,因为在启动线程之前先接受互斥锁,线程只能在主线程处于等待状态时锁定互斥锁。遗憾的是,围绕它的pthread实现仍然没有'sem_t'。 – 2012-07-13 14:37:29

2

障碍应该很好地做到这一点。由于您在注释中提到需要在Win32上提供支持,因此最新的Win32 pthread支持障碍,所以您不必编写自己的包装来获得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是要走的路。