2012-05-11 35 views
0

我是新來的C線程中的pthreads,我正在寫一個簡單的程序,它可以並行地在多個文件中查找單詞。但是,只要我輸入多個文件,輸出就會變化,這表明我的代碼中沒有修復競爭條件。你能幫我解決嗎?C中的pthreads的競態條件

下面的代碼片段是主要的,使pthreads。

int i = 0; 
char *word = "Pluto"; //Word to be found 

Message messages[argc-1]; 
pthread_t threads[argc-1]; 
for(i; i < argc - 1; i++){ 
    messages[i].file = argv[i + 1]; 
    messages[i].word = word; 
    messages[i].fp = fopen(argv[i + 1], "r"); 
    int iret = pthread_create(&threads[i], NULL, threadFindWord, (void*) &(messages[i])); 
}for(i = 0; i < argc - 1; i++){ 
    pthread_join(threads[i],NULL); 
} 

每個線程調用該函數:

Message *msg; 
msg = (Message *) ptr; 

int numFound = ffindWord(msg->fp, msg->word); 

printf("File %s has %i occurences of the word %s\n", msg->file, numFound, msg->word); 

fclose(msg->fp); 
pthread_exit(NULL); 

下面是在文件中查找單詞的代碼)

int findWord(char * file, char * word){ 
char * current = strtok(file, " ,.\n"); 
int sum = 0; 
while (current != NULL){ 
    //printf("%s\n", current); 
    if(strcmp(current, word) == 0) 
     sum+=1; 
    current = strtok(NULL, " ,.\n"); 
} 
return sum; 
} 



int ffindWord(FILE *fp, char *word){ 

fseek(fp, 0, SEEK_END); 
long pos = ftell(fp); 
fseek(fp, 0, SEEK_SET); 
char *bytes = malloc(pos); 
fread(bytes, pos, 1, fp); 
bytes[pos-1] = '\0'; 

int sum = findWord(bytes, word); 

free(bytes); 
return sum; 
} 

爲了澄清,問題是,我得到連續運行程序時會產生不同的結果。呼叫 $ programname file1 file2 打印與剛剛調用的同一個呼叫不同的結果。但請注意,只有一個文件通過時,程序才能工作。

任何幫助表示讚賞。

+2

輸出如何變化?來自同一文件或混合輸出的不同結果? – hmjd

+0

@hmjd是的輸出變化。例如:'$ ./a。out blu bla'輸出文件藍色文件有0個出現在Pluto文件中的文件bla有5個出現在Pluto這個文件後面和在$ $ /a.out blu bla'輸出文件後面有相同的命令File bla有1次出現的冥王星文件# '文件藍光有4個詞冥王星' – pretobomba

+0

沒有必要投到或退出'void *' –

回答

1

這將導致不確定的行爲,因爲它超越了messagesthreads陣列的末尾:

Message messages[argc-1]; 
pthread_t threads[argc-1]; 
for(i; i < argc; i++){ 

,並可能是問題的原因。當只有1個線程執行時,它可能會偶然運行。

嘗試改用(或類似的東西):

int i; 
Message messages[argc-1]; 
pthread_t threads[argc-1]; 
for(i = 1; i < argc; i++) 
{ 
    messages[i - 1].file = argv[i]; 
    messages[i - 1].word = word; 
    messages[i - 1].fp = fopen(argv[i], "r"); 
    int iret = pthread_create(&threads[i - 1], 
           NULL, 
           threadFindWord, 
           (void*)&(messages[i - 1])); 
} 

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

嗯..循環應該從0運行到(argc-1)以便爲陣列生成正確的基於0的索引。 –

+0

@MartinJames,在我的例子中,解決方案'i-1'用於在第一個'for'中爲'messages'和'threads'建立索引。 OP要跳過'argv [0]',因爲它是程序名稱。 – hmjd

+0

如果索引被保持爲0,則更容易理解和編碼。在**中爲()做一個簡單的if(i == 0)continue **。 – tuxuday

0

爲了避免在隨機的方式混合在一起,每個線程的輸出,你需要緩衝每個線程的輸出,然後在時間顯示出來一個。

對於你的情況,要做到這將是最簡單的方法,以一個char *thread_output場(和thread_output_size場)添加到Message結構,然後做這樣的事情在你的主線程:

for(i = 0; i < argc - 1; i++) 
{ 
    pthread_join(threads[i],NULL); 
    printf("%s", messages[i - 1].thread_output); 
} 

你可能還需要實現一個功能,確保緩衝區足夠大,然後使用vsnprintf()向緩衝區添加新文本,以便您可以像使用printf()一樣使用它。

例如:

void add_thread_output(int thread_number, const char *template, ...) { 
    int old_length; 
    int length; 
    char *temp; 

    va_start(ap, template); 
    length = vsnprintf(NULL, 0, template, ap); 
    va_end(ap); 

    old_length = messages[thread_number - 1].thread_output_size; 
    temp = realloc(messages[thread_number - 1].thread_output, old_length + length + 1); 
    if(temp == NULL) { 
     /* Set a flag or something */ 
    } else { 
     va_start(ap, template); 
     vsnprintf(&temp[old_length], length + 1, template, ap); 
     va_end(ap); 
     messages[thread_number - 1].thread_output_size += length; 
     messages[thread_number - 1].thread_output = temp; 
    } 
} 

注:上面的任何示例代碼是例如僅的目的,並沒有進行測試或保證編譯或工作,也並不一定是最有效的這樣做的方式。例如。分配比您需要的空間更多的空間(避免在每次將某些內容添加到線程的輸出緩衝區時執行realloc())可能是一個好主意。