2012-07-13 103 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是要走的路。