2017-09-21 38 views
1

我讀到APUE 3rd,11.6.1互斥,存在關於鎖定一個例子,在本章中解鎖互斥:如何在Linux中使用pthread_mutex_destroy安全並正確地銷燬互斥鎖​​?

struct foo { 
    int    f_count; 
    pthread_mutex_t f_lock; 
    int    f_id; 
    /* ... more stuff here ... */ 
}; 

struct foo * 
foo_alloc(int id) /* allocate the object */ 
{ 
    struct foo *fp; 

    if ((fp = malloc(sizeof(struct foo))) != NULL) { 
     fp->f_count = 1; 
     fp->f_id = id; 
     if (pthread_mutex_init(&fp->f_lock, NULL) != 0) { 
      free(fp); 
      return(NULL); 
     } 
     /* ... continue initialization ... */ 
    } 
    return(fp); 
} 

void 
foo_hold(struct foo *fp) /* add a reference to the object */ 
{ 
    pthread_mutex_lock(&fp->f_lock); 
    fp->f_count++; 
    pthread_mutex_unlock(&fp->f_lock); 
} 

void 
foo_rele(struct foo *fp) /* release a reference to the object */ 
{ 
    pthread_mutex_lock(&fp->f_lock); 
    if (--fp->f_count == 0) { /* last reference */ 
     pthread_mutex_unlock(&fp->f_lock); 
     pthread_mutex_destroy(&fp->f_lock); 
     free(fp); 
    } else { 
     pthread_mutex_unlock(&fp->f_lock); 
    } 
} 

在foo_rele,有調用pthread_mutex_unlock和pthread_mutex_destroy之間的競爭條件,所以,乙線程可以調用pthread_mutex_unlock和pthread_mutex_destroy之間的pthread_mutex_lock在線程這將導致未定義的行爲(試圖銷燬鎖定的互斥鎖導致未定義的行爲)。我對嗎?如果我是對的,那麼,如何使它正常工作,或者如何在Linux中使用pthread_mutex_destroy安全並正確地銷燬互斥鎖​​?

回答

1

pthread_mutex_destroy()的POSIX規範說:

它應是安全的銷燬解鎖初始化的互斥鎖。

這意味着如果線程B在foo_rele()if語句的else子句中調用pthread_mutex_unlock()那麼它是安全的線程A調用pthread_mutex_destroy(),因爲它只能有線程B的pthread_mutex_unlock()電話後到達那裏解鎖了互斥。

所有這一切都假定引用計數是正確的,這樣在線程A解鎖互斥鎖之後,其他某個線程無法將計數從0增加到1。換句話說,在引用計數下降到0的地方,不可能有另一個線程可能會調用foo_hold()

APUE示例代碼之後提到這一點的解釋:

在這個例子中,我們忽略了線程如何連接的第二調用foo_hold之前的對象。儘管引用計數爲零,但如果在調用foo_hold時在互斥體上阻塞了另一個線程,則foo_rele釋放對象的內存將是錯誤的。我們可以通過確保在釋放內存之前找不到對象來避免此問題。我們將在後面的例子中看到如何做到這一點。

+0

能否請您參考** 11.6.2死鎖避免**檢查示例代碼,我覺得線程只能通過調用'foo_find'增加對象引用,而不是'foo_hold'直接,我說的對? – cong

+1

是的只有'foo_find()'調用'foo_hold()'。當'foo_rele()'發現它要將一個refcount遞減爲0時,它會在持有互斥鎖的同時從哈希表中刪除項目,所以沒有其他線程可以再次獲得指向該'foo'對象的指針。一旦發生這種情況,可以解鎖互斥鎖,'foo'對象中的互斥鎖可以被銷燬,並且'foo'對象本身被釋放,因爲沒有別的東西可以再獲得'foo'對象。 –

+0

**第11.6.2節中的示例代碼有兩個版本,在版本一中,代碼'foo_hold(fp);''foo_find''應替換爲: 'pthread_mutex_lock(&fp-> f_lock) ; fp-> f_count ++; pthread_mutex_unlock(&fp-> f_lock);' 和函數'foo_hold'應該被移除。 在第二版中,功能'foo_hold'應該被移除,另一方面,這是沒有用的和危險的,用戶可能會濫用界面,對吧? – cong