多個執行緒同時訪問共享資料時可能會衝突,比如兩個執行緒都要把某個全域性變數增加1,這個操作在某平台需要三條指令完成:
假設兩個執行緒在多處理器平台上同時執行這三條指令,則可能導致下圖所示的結果,最後變數只加了一次而非兩次。
如下例子就演示了這一過程:
#include
#include
#include
intcounter; /* incremented by threads */
void*doit(void*vptr)
returnnull;
}intmain(intargc, char**argv)
在這個例子中,雖然每個執行緒都給counter加了100,但由於結果的互相覆蓋,最終輸出值不是200,而是100。
tianfang@linux-k36c:/mnt/share/test> run
counter = 100
tianfang@linux-k36c:/mnt/share/test>
互斥鎖mutex
對於多執行緒的程式,訪問衝突的問題是很普遍的,解決的辦法是引入互斥鎖(mutex,mutual exclusive lock),獲得鎖的執行緒可以完成"讀-修改-寫"的操作,然後釋放鎖給其它執行緒,沒有獲得鎖的執行緒只能等待而不能訪問共享資料,這樣"讀-修改-寫"三步操作組成乙個原子操作,要麼都執行,要麼都不執行,不會執行到中間被打斷,也不會在其它處理器上並行做這個操作。
#include
pthread_mutex_t mutex = pthread_mutex_initializer;
intpthread_mutex_destroy(pthread_mutex_t *mutex);
intpthread_mutex_init(pthread_mutex_t *restrictmutex, constpthread_mutexattr_t *restrictattr);
intpthread_mutex_lock(pthread_mutex_t *mutex);
intpthread_mutex_trylock(pthread_mutex_t *mutex);
intpthread_mutex_unlock(pthread_mutex_t *mutex);
從名字中基本上就能看出來該如何使用,這裡就只是以上面的例子為例,用mutex將其改造一下:
#include
#include
#include
intcounter; /* incremented by threads */
pthread_mutex_t counter_mutex = pthread_mutex_initializer;
void*doit(void*vptr)
returnnull;
}intmain(intargc, char**argv)
這次的執行結果就是正確的了:
tianfang@linux-k36c:/mnt/share/test> run
counter = 200
tianfang@linux-k36c:/mnt/share/test>
條件變數condtion
前面介紹的mutext主要用於實現互斥,在多執行緒的場景中往往還有同步的需求。例如,在生產者和消費者的例子中,消費者需要等待生產者產生資料後才能消費。
單用mutex不能解決同步的問題:假如消費者先獲取到鎖,但此時並沒有資源可用,如果把鎖佔著不放,生產者無法獲取鎖,此時就成了死鎖;如果立即釋放,並重新進入,則會成為輪詢而消耗cpu資源。
在pthread庫中通過可條件變數(condition variable)來阻塞等待乙個條件,或者喚醒等待這個條件的執行緒。condition variable用pthread_cond_t型別的變數表示,其相關函式如下:
#include
pthread_cond_t cond = pthread_cond_initializer;
intpthread_cond_destroy(pthread_cond_t *cond);
intpthread_cond_init(pthread_cond_t *restrictcond, constpthread_condattr_t *restrictattr);
intpthread_cond_timedwait(pthread_cond_t *restrictcond, pthread_mutex_t *restrictmutex, conststructtimespec *restrictabstime);
intpthread_cond_wait(pthread_cond_t *restrictcond, pthread_mutex_t *restrictmutex);
intpthread_cond_broadcast(pthread_cond_t *cond);
intpthread_cond_signal(pthread_cond_t *cond);
從它的wait函式可以看到,condtion是和mutext一起使用的,基本流程如下:
消費者獲取資源鎖,如果當前無可用資源則呼叫cond_wait函式釋放鎖,並等待condtion通知。
生產者產生資源後,獲取資源鎖,放置資源後嗲用cond_signal函式通知。並釋放資源鎖。
消費者的cond_wait函式等到condtion通知後,重新獲取資源鎖,消費資源後再次釋放資源鎖。
從中可以看到,mutex用於保護資源,wait函式用於等待訊號,signal和broadcast函式用於通知訊號。其中wait函式中有一次對mutex的釋放和重新獲取操作,因此生產者和消費者並不會出現死鎖。
#include
#include
#include
#include
#include
usingnamespacestd;
queue buffer;
pthread_cond_t has_product = pthread_cond_initializer;
pthread_mutex_t lock = pthread_mutex_initializer;
void* consumer(void*p)
}void* producer(void*p)
}intmain(intargc, char*argv)
訊號量semaphore
mutex變數是非0即1的,可看作一種資源的可用數量,初始化時mutex是1,表示有乙個可用資源,加鎖時獲得該資源,將mutex減到0,表示不再有可用資源,解鎖時釋放該資源,將mutex重新加到1,表示又有了乙個可用資源。
#include
intsem_init(sem_t *sem, intpshared, unsignedintvalue);
intsem_wait(sem_t *sem);
intsem_trywait(sem_t *sem);
intsem_post(sem_t * sem);
intsem_destroy(sem_t * sem);
這裡以乙個有限資源池的生產者和消費者的例子演示一下訊號量的用法:
#include
#include
#include
#include
#include
#definenum 5
intqueue[num];
sem_t blank_number, product_number;
void*producer(void*arg)
}void*consumer(void*arg)
}intmain(intargc, char*argv)
讀寫鎖(reader-writer lock)
讀寫鎖也是一種執行緒間的互斥操作,但和互斥鎖不同的是,它分為讀和寫兩種模式,其中寫模式是獨佔資源的(和互斥鎖一樣),而讀模式則是共享資源的,此時允許多個讀者,從而提高併發性。
由於一時找不到合適的小例子來介紹它,並且讀寫鎖的模型也是比較容易理解的,這裡就不舉例了。
Linux系統程式設計 執行緒同步與互斥 互斥鎖
在多工作業系統中,同時執行的多個任務可能都需要使用同一種資源。這個過程有點類似於,公司部門裡,我在使用著印表機列印東西的同時 還沒有列印完 別人剛好也在此刻使用印表機列印東西,如果不做任何處理的話,列印出來的東西肯定是錯亂的。下面我們用程式模擬一下這個過程,執行緒一需要列印 hello 執行緒二需要...
linux程式設計 執行緒 互斥鎖
執行緒間同步機制 互斥鎖通訊機制 互斥以排他方式防止共享資料被併發修改。1 在訪問該資源前,首先申請該互斥鎖,如果該互斥處於開鎖狀態,則申請到該鎖物件,並立即占有該鎖,以防止其他執行緒訪問該資源。如果該互斥鎖處於鎖定狀態,預設阻塞等待。2 只有鎖定該互斥鎖的程序才能釋放該互斥鎖,其他執行緒的釋放操作...
Linux多執行緒程式設計 執行緒互斥鎖
通過下面的練習加深對執行緒的概念的理解,同時明確執行緒的控制。從而進一步了解執行緒的互斥,並學會利用pthread庫。定義乙個用於互斥的互斥鎖 和乙個主函式和兩個子執行緒都能訪問的共享變數,乙個主函式和兩個用來建立子執行緒的子函式 在主函式中定義兩個子執行緒id的變數,初始化互斥鎖,建立對應函式的子...