2016-04-21 106 views
0

考慮下一個代碼塊 -怪異pthread_mutex_t行爲

#include <iostream> 

using namespace std; 

int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

int main() { 

    pthread_t p1; 
    pthread_t p2; 
    pthread_t p3; 

    pthread_create(&p1, NULL, foo, NULL); 
    pthread_create(&p2, NULL, foo, NULL); 
    pthread_create(&p3, NULL, foo, NULL); 

    pthread_join(p1, NULL); 
    pthread_join(p2, NULL); 
    pthread_join(p3, NULL); 

    return 0; 
} 

我只是創建了三個pthreads並給他們所有相同功能foo,在此希望每個線程,在其反過來,將打印和遞減sharedIndex

但這是輸出 -

10 
9 
8 
7 
6 
5 
4 
3 
2 
1 
0 
-1 
-2 
  • 我不明白爲什麼當sharedIndex 達到0
  • sharedIndexmutex保護的進程不會停止。在它變成0之後它是如何訪問的?線程是不是應該直接跳至return NULL;

編輯

此外,似乎只有第一個線程遞減sharedIndex。 爲什麼不是每個線程都在遞減共享資源? 這裏有一個修正後的輸出 -

Current thread: 140594495477504 
10 
Current thread: 140594495477504 
9 
Current thread: 140594495477504 
8 
Current thread: 140594495477504 
7 
Current thread: 140594495477504 
6 
Current thread: 140594495477504 
5 
Current thread: 140594495477504 
4 
Current thread: 140594495477504 
3 
Current thread: 140594495477504 
2 
Current thread: 140594495477504 
1 
Current thread: 140594495477504 
0 
Current thread: 140594495477504 
Current thread: 140594478692096 
Current thread: 140594487084800 

我希望所有的線程將遞減共享源代碼 - 這意味着,每一個開關CONTEX,不同的線程將訪問資源,做它的事。

+2

嘗試在程序的最後創建線程和「pthread_mutex_destroy」之前調用'pthread_mutex_init' – mausik

+0

爲什麼不嘗試在代碼中修復未定義的行爲並查看它是否有幫助? –

+0

我確實改變了代碼,它工作。但仍然 - 只有一個線程遞減資源 –

回答

4

Th程序的行爲是未定義的。

您還沒有初始化互斥鎖。你需要或者調用pthread_mutex_init或靜態初始化:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 

你讀這個變量的臨界區外:

while(sharedIndex >= 0) 

這意味着,而另一個線程正在更新它,你可以讀取垃圾值。在鎖定互斥鎖並擁有對其的獨佔訪問權限之前,您不應該讀取共享變量。

編輯:

似乎只有第一個線程遞減sharedIndex

這是因爲不確定的行爲。解決上述問題,你應該看到其他線程運行。

使用您當前的代碼,編譯器可以假定sharedIndex從不會被其他線程更新,所以它不會重新讀取它,但只是讓第一個線程運行十次,然後其他兩個線程每個運行一次。

意思是,每一個交換機,一個不同的線程都會訪問資源並執行它的操作。

無法保證pthread mutexes的公平行爲。如果你想保證每個線程輪流運行的循環行爲,那麼你就需要自己強加這個行爲。通過讓另一個共享變量(也許是一個條件變量)來說明它將運行哪個線程,並阻止其他線程,直到輪到他們。

3

線程將掛在pthread_mutex_lock(&mutex);等待獲取鎖定。一旦線程遞減到0並釋放鎖,下一個等待鎖的線程將執行它的業務(使值爲-1),並且對於下一個線程(使值爲-2)相同。

你需要改變你的邏輯檢查值和鎖定互斥鎖。

3
int sharedIndex = 10; 
pthread_mutex_t mutex; 

void* foo(void* arg) 
{ 
    while(sharedIndex >= 0) 
    { 
     pthread_mutex_lock(&mutex); 

     cout << sharedIndex << endl; 
     sharedIndex--; 

     pthread_mutex_unlock(&mutex); 
    } 

    return NULL; 
} 

根據這個代碼sharedIndex是所有線程共享資源

因此,每個對它的訪問(包括讀寫)都應該被互斥包裝。 否則,假設所有線程同時採樣sharedIndex並且其值爲1

然後,所有線程輸入while循環,每個線程將sharedIndex減1,最後將其導向-2

編輯

可能修復(如可能的選項之一):

bool is_positive; 
do 
{ 
    pthread_mutex_lock(&mutex); 

    is_positive = (sharedIndex >= 0); 
    if (is_positive) 
    { 
     cout << sharedIndex << endl; 
     sharedIndex--; 
    } 

    pthread_mutex_unlock(&mutex); 
}while(is_positive); 

EDIT2

注意,您必須初始化互斥鎖:

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
+1

直到最後一段,它贏得了讚賞。'volatile'在這裏沒有用,編譯器永遠不會移動互斥鎖之外的變量訪問(除非編譯器完全被破壞),所以使用寄存器無關緊要,只要結果結束內存在關鍵部分的末尾。 –

+0

謝謝你,你可能的解決辦法做到了。 –

+0

@JonathanWakely編譯器可能(可能)在函數的開始處將'sharedIndex'移動到'eax',並在'return'之前將其存回'sharedIndex'。也許它不會這樣做,但我不知道是否可以應用任何「法律」,這將阻止編譯器這樣做。如果編譯器知道'pthread_mutex_lock'和'pthread_mutex_unlock'的實現,可能會得出結論:沒有任何影響'sharedIndex'和'sharedIndex'的結果不影響這些函數。所以這樣的優化是可能的。 –