2012-07-10 83 views
1

我不知道什麼是錯與下面的程序,但它不會打印每一個語言一次,但隨機一些更多的時候,多一些少一些不會打印併發與並行線程,一個典型的錯誤行爲

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 



char *messages[] = { 
    "English: Hello World!", 
    "French: Bonjour, le monde!", 
    "Spanish: Hola al mundo", 
    "Klingon: Nuq neH!", 
    "German: Guten Tag, Welt!", 
    "Russian: Zdravstvytye, mir!", 
    "Japan: Sekai e konnichiwa!", 
    "Latin: Orbis, te saluto!" 
}; 
#define NUM_MESSAGES (sizeof(messages)/sizeof(char*)) 


void *PrintHello(void *messageid) 
{ 
    pthread_t taskid; 
    int *id_ptr, message_num; 

    taskid = pthread_self(); 
    printf("This is thread with ID %lu.\n", taskid); 

    message_num = *((int *) messageid); 
    printf("%s \n", messages[message_num]); 

    pthread_exit(NULL); 
} 

int main(int argc, char *argv[]) 
{ 
    pthread_t threads[NUM_MESSAGES]; 
    int rc, i; 


    for(i = 0; i < NUM_MESSAGES; i++) { 
     void * argument = (void*) &i; 
     rc = pthread_create(&threads[i], NULL, PrintHello, argument); 
     if(rc) { 
      printf("ERROR; return code from pthread_create() is %d\n", rc); 
      exit(-1); 
     } 
    } 




    for(i = 0; i < NUM_MESSAGES; i++) { 
     pthread_join(threads[i], NULL); 
    } 

    pthread_exit(NULL); 
} 

我想這個問題在某種程度上與參數指針有關。我試圖鎖定不同的部分,但沒有成功。

回答

4

您正在傳遞變量i的地址,其中主線程不斷變化。所以你是在調度的擺佈:在你的線程有機會運行之前,任何人都會改變i嗎?

相反,你可以直接嘗試傳遞字符串,大大簡化了代碼:

rc = pthread_create(&threads[i], NULL, PrintHello, messages[i]); 

void *PrintHello(void *arg) 
{ 
    char *msg = arg; 
    printf("%s\n", msg); 

    return NULL; 
} 

還有另一種,較差的替代,你傳遞的i的實際值作爲參數(而不是它的地址) 。

+0

謝謝您的幫助。這對我有效。有沒有試圖用鎖來解決這個問題?我在設置「參數」之前嘗試鎖定,並在線程創建後解鎖,但沒有工作:/我知道這將是協調的想法,但只是爲了我的利益:) – smoes 2012-07-10 07:08:49

+1

@smoes不,鎖定在這裏是不可行的。一旦你通過了停止使用'i'的點,你將不得不在創建線程和解鎖線程之前以某種方式鎖定。 – cnicutar 2012-07-10 07:10:43

+0

@smoes,不使用pthread mutexes - pthread mutexes必須由鎖定它們的同一個線程釋放,但在這裏您需要等待另一個線程在解鎖之前捕獲該值。而且,通過只讓一個線程立即運行,你就失去了線程的很多好處。通常的解決方案是爲每個線程分配一個新的緩衝區,然後將其傳遞給pthread_create。新線程然後負責釋放緩衝區。這種方式不需要鎖。 – bdonlan 2012-07-10 07:11:34

0

這是一個典型的race條件。
確切地說,在創建新線程之後,線程的執行順序決定了程序結果。有兩種情況:

  1. 當線程功能PrintHello是下一個增量i++main線程分配void * argument = (void*) &i;之前執行,它輸出你所期望的。

  2. 當新線程PrintHello已創建,但沒有獲得計劃直到main線程執行的下一個i++。在這種情況下,您在線程函數argument中的引用,即message_num = *((int *) messageid);不是您所期望的。

一種用於此的解決方案是動態地分配(使用mallocargument,和free它們在線程功能使用後。

+0

downvoter可以解釋爲什麼? – WiSaGaN 2014-09-19 09:31:30

0

一個通過標量整數參數到線程功能的方法是(AB-)使用下面的文本從C99標準的§6.3.2.3:

(5)的整數可以被轉換爲任何指針類型。除前面指定外, 結果是實現定義的,可能未正確對齊,可能不指向引用類型的實體,可能是陷阱表示。

除第二句,下面的代碼應該在大多數系統上運行的最後一部分:

int int_val; 
... 
pthread_create(..., ..., thread_func, (void *)int_val); 

,然後在thread_func轉換回整數:

void *thread_func (void *data) 
{ 
    int int_arg = (int)data; 
    ... 
} 
+0

我完全是這樣解決問題:) – smoes 2012-07-11 14:33:28