2016-10-12 115 views
0

我在Linux上用C語言開發的代碼存在問題。C - 併發問題

我有這樣的結構:

typedef struct _my_sem { 
    unsigned int valid; 
    pthread_mutex_t m; 
} my_sem; 

和以下功能(錯誤管理不報告):

void my_sem_init(my_sem *s); 
void my_sem_destroy(my_sem *s); 
void my_sem_lock(my_sem *s); 
void my_sem_unlock(my_sem *s); 


void my_sem_init(my_sem *s) 
{ 
    pthread_mutex_create(&s->m); 
    s->valid = 1; 
} 

void my_sem_destroy(my_sem *s) 
{ 
    if (s->valid) { 
     pthread_mutex_destroy(&s->m); 
     s->valid = 0; 
    } 
} 

void my_sem_lock(my_sem *s) { 
    if (s->valid) 
     pthread_mutex_lock(&s->m); 
} 

void my_sem_unlock(my_sem *s) { 
    if (s->valid) 
     pthread_mutex_unlock(&s->m); 
} 

此代碼具有併發性的問題。如果有人試圖鎖定my_sem ,並且在某人銷燬objcet的同時,呼叫將失敗。

我該如何解決這個併發問題?

+0

更改錯誤管理。嚴重的是,只需添加返回值檢查和perror來查看出了什麼問題。這就是錯誤出現的原因。 – LambdaBeta

+1

這是很多線程可能試圖同時鎖定的常見情形。但爲什麼多個線程會試圖同時銷燬呢?當然,在「結束」的單一線程可以做到這一點? –

+0

奇怪的是有一個線程試圖摧毀其他線程需要的資源... – LPs

回答

1

您提出的設計有一個雞與蛋的問題:成員my_sem.valid旨在向多個線程指示給定的互斥體是否處於有效狀態,但訪問這些成員的多個線程在創建數據競爭時訪問是寫入,並且訪問不是相對於彼此排序的(例如通過用互斥體保護訪問)。

而且,你無法通過調整valid類型一個可以原子更新解決了這個問題,因爲互斥和valid更新的初始化/最終確定必須爲你的功能正常工作不可分割的單元來執行。這個要求使用另一個互斥量,信號量或一些類似的同步輔助。

但是,通過向結構中添加額外的同步對象不能解決任何問題,因爲如果不能依賴現有互斥鎖處於已知狀態,那麼您也不能依賴新對象的狀態。因此,爲了挽救這種設計,您需要一個全局互斥或信號量,用於保護這四個函數中的每一個函數的主體。

下面是一個替代方法:不要發出未初始化的互斥鎖,也不要銷燬正在使用(或可能仍在使用中)的互斥鎖。如有必要,您甚至可以使用互斥鎖本身來保護它是否仍在使用的測試,只要您構造代碼以避免在發現該互斥鎖未找到之後重新測試任何互斥鎖的使用中狀態的可能性使用。這將需要改變你如何使用你的互斥體,你的互斥體管理函數,以及你的struct(如果你保留結構的話)。