2016-03-21 124 views
0

此程序的目標是複製用戶輸入單詞的字符串,並使用多線程處理單詞。每個線程複製每四個單詞,例如第一個線程複製第一個和第五個單詞,第二個複製第二個和第六個單詞等等。我對互斥體做了相當多的研究,我相信我已經正確實施了互斥鎖然而,當它打印時,字符串仍然顯得雜亂無章。有人可以闡明爲什麼線程不同步嗎?使用互斥鎖的Pthread同步不能正確同步單詞

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

void *processString(void *); 

char msg1[100]; 
char msg2[100]; 
char * reg; 
char * token; 
char * tokens[10]; 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t = PTHREAD_COND_INITIALIZER; 

int main(){ 
    int i = 0, j; 
    pthread_t workers[4]; 

    printf("Please input a string of words separated by whitespace characters: \n"); 
    scanf("%99[^\n]", msg1); //take in a full string including whitespace characters 

    //tokenize string into individual words 
    token = strtok(msg1, " "); 
    while(token != NULL){ 
     tokens[i] = (char *) malloc (sizeof(token)); 
     tokens[i] = token; 
     token = strtok(NULL, " "); 
     i++; 
    } 

    for(j = 0; j < 4; j++){ 
     if(pthread_create(&workers[j], NULL, processString, (void *) j)) 
      printf("Error creating pthreads"); 
    } 

    for(i = 0; i < 4; i++){ 
     pthread_join(workers[i], NULL); 
    } 
    pthread_mutex_destroy(&lock); 

    printf("%s\n", msg2); 

    return 0; 
} 

//each thread copies every fourth word 
void *processString(void *ptr){ 
    int j = (int) ptr, i = 0; 

    pthread_mutex_lock(&lock); 

    while(tokens[i * 4 + j] != NULL){ 
     reg = (char *) malloc (sizeof(tokens[i * 4 + j])); 
     reg = tokens[i * 4 + j]; 
     strcat(msg2, reg); 
     strcat(msg2, " "); 
     i++; 
    } 

    pthread_mutex_unlock(&lock); 
    return NULL; 
} 
+1

如果你想控制線程的運行順序,你需要類似條件變量或信號量的東西。互斥體只確保相互排斥,而不是排序。 – EOF

+1

什麼EOF說。但是,你的代碼也會泄漏內存。在其循環的每次迭代中,函數'processString()'分配一塊內存,將指向該塊的指針賦值給變量'reg',然後立即用另一個指針覆蓋該指針,從而失去指向分配的唯一指針塊。然而,從某種意義上說,這也是一樣,因爲你爲一個*指針*分配了足夠的空間,而似乎(誤導)的意圖是爲指向字符串分配足夠的空間。 –

+1

另外,您的鎖定是錯誤的。一旦每個線程獲得互斥鎖,它將在釋放它之前執行所有*工作。互斥對於防止結果字符串被破壞是有效的,從這個意義上說,您正確使用它。但是,只有當輸入中少於五個單詞時纔有可能獲得所需的輸出。 –

回答

1

由於@EOF在評論中寫道,互斥提供只有互斥。它們阻止多個協作線程同時運行,但它們並不固有地控制它們被這些線程獲取的順序。另外,正如我在自己的評論中所描述的那樣,互斥鎖提供互斥功能:如果一個線程持有互斥鎖,則其他線程將無法獲取該互斥鎖,也不會繼續嘗試這樣做,直到該互斥鎖被釋放。

沒有本地同步對象,它直接提供讓線程輪流使用。畢竟,這通常不是你想要的。你可以用信號量來安排它,但是當你添加更多的線程時,它會很快變得混亂。一個非常乾淨的解決方案涉及使用共享的全局變量來指示要運行哪個線程。對變量的訪問必須由互斥體保護,因爲所涉及的所有線程都必須讀取和寫入它,但是存在一個問題:如果當前擁有該互斥體的線程不是要輪到它運行的線程?

所有線程都有可能循環,持續獲取互斥鎖,測試變量以及繼續或釋放互斥鎖。不幸的是,如此繁忙的等待往往表現得非常糟糕,並且一般來說,您無法確信在執行的任何給定點上可以取得進展的線程都能在有限的時間內獲得互斥。

這是condition variables進來的地方。條件變量是一個同步對象,它允許任意數量的線程掛起活動,直到滿足某些條件爲止,如另一個未掛起的線程所判斷。使用這樣的工具可以避免性能枯竭的忙碌等待,在您的情況下,它可以幫助確保您的所有線程都有機會在有限的時間內運行。條件變量通用每線程使用模型如下:

  1. 獲得互斥保護,通過該判斷是否能繼續進行
  2. 試驗I是否可以進行共享變量(多個)。如果是,請跳至步驟5.
  3. 我現在無法進行。對條件變量執行等待。
  4. 我已經從等待中驚醒;回到步驟2.
  5. 做我需要做的工作。
  6. 廣播一個信號喚醒等待條件變量的所有線程。
  7. 釋放互斥鎖。

雖然可能存在差異,但我建議您不要因此而有所不同,除非您確切知道您爲什麼要這麼做,以及您確切知道爲什麼您想要的變化是安全的。還要注意,當一個線程在與一個給定的互斥體相關聯的條件變量上執行一個等待時,它會在等待時自動釋放該互斥體,並在等待返回之前重新獲取該互斥體。這允許其他線程同時進行,特別是等待相同的條件變量。

因爲它適用於你的問題,所以你希望你的線程測試的共享狀態是前面提到的變量,它指示了它是哪個線程,你希望你的線程等待的條件是它已經變得不同了線程的轉變(但這是隱含的,你使用條件變量的方式;條件變量本身是通用的)。還要注意,這意味着每個線程在發出其他線程信號之前必須完成的部分工作是更新線程的狀態。而且由於每個線程可能需要多次輪換,您將需要將整個程序包裝在一個循環中。

+0

謝謝!那正是我需要的。我的程序正在做我所需要的。 :) – Turbotec