所謂同步,即同時起步,協調一致。不同的物件,對"同步"的理解方式略有不同。如,裝置同步,是指在兩個裝置之間規定乙個共同的時間參考;資料庫同步,是指讓兩個或多個資料庫內容保持一致,或者按需要部分保持一致;檔案同步,是指讓兩個或多個資料夾裡的檔案保持一致。等等
而,程式設計中、通訊中所說的同步與生活中大家印象中的同步概念略有差異。"同"字應是指協同、協助、互相配合。主旨在協同步調,按預定的先後次序執行。
同步即協同步調,按預定的先後次序執行。
執行緒同步,指乙個執行緒發出某一功能呼叫時,在沒有完成工作、得到結果之前,該呼叫不返回。同時其它執行緒為保證資料一致性,不能呼叫該功能。
舉例1: 銀行存款 5000。櫃檯,折:取3000;提款機,卡:取 3000。剩餘:2000
舉例2: 記憶體中100位元組,執行緒t1欲填入全1, 執行緒t2欲填入全0。但如果t1執行了50個位元組失去cpu,t2執行,會將t1寫過的內容覆蓋。當t1再次獲得cpu繼續 從失去cpu的位置向後寫入1,當執行結束,記憶體中的100位元組,既不是全1,也不是全0。
產生的現象叫做"與時間有關的錯誤"(time related)。為了避免這種資料混亂,執行緒需要同步。
"同步"的目的,是為了避免資料混亂,解決與時間有關的錯誤。實際上,不僅執行緒間需要同步,程序間、訊號間等等都需要同步機制。
因此,所有"多個控制流,共同操作乙個共享資源"的情況,都需要同步。
1. 資源共享(獨享資源則不會)
2. 排程隨機(意味著資料訪問會出現競爭)
3. 執行緒間缺乏必要的同步機制。
以上3點中,前兩點不能改變,欲提高效率,傳遞資料,資源必須共享。只要共享資源,就一定會出現競爭。只要存在競爭關係,資料就很容易出現混亂。
所以只能從第三點著手解決。使多個執行緒在訪問共享資源的時候,出現互斥。
linux中提供一把互斥鎖mutex(也稱之為互斥量)。
每個執行緒在對資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結束解鎖。
資源還是共享的,執行緒間也還是競爭的,
但通過"鎖"就將資源的訪問變成互斥操作,而後與時間有關的錯誤也不會再產生了。
但,應注意:同一時刻,只能有乙個執行緒持有該鎖。
當a執行緒對某個全域性變數加鎖訪問,b在訪問前嘗試加鎖,拿不到鎖,b阻塞。c執行緒不去加鎖,而直接訪問該全域性變數,依然能夠訪問,但會出現資料混亂。
所以,互斥鎖實質上是作業系統提供的一把"建議鎖"(又稱"協同鎖"),建議程式中有多執行緒訪問共享資源的時候使用該機制。但,並沒有強制限定。
因此,即使有了mutex,如果有執行緒不按規則來訪問資料,依然會造成資料混亂。
pthread_mutex_init函式
pthread_mutex_destroy函式
pthread_mutex_lock函式
pthread_mutex_trylock函式
pthread_mutex_unlock函式
以上5個函式的返回值都是:成功返回0, 失敗返回錯誤號。
pthread_mutex_t 型別,其本質是乙個結構體。為簡化理解,應用時可忽略其實現細節,簡單當成整數看待。
pthread_mutex_t mutex; 變數mutex只有兩種取值1、0。
pthread_mutex_init函式
初始化乙個互斥鎖(互斥量) ---> 初值可看作1
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
參1:傳出引數,呼叫時應傳 &mutex
restrict關鍵字:只用於限制指標,告訴編譯器,所有修改該指標指向記憶體中內容的操作,只能通過本指標完成。不能通過除本指標以外的其他變數或指標修改
參2:互斥量屬性。是乙個傳入引數,通常傳null,選用預設屬性(執行緒間共享)。 參apue.12.4同步屬性
靜態初始化:如果互斥鎖 mutex 是靜態分配的(定義在全域性,或加了static關鍵字修飾),可以直接使用巨集進行初始化。e.g.
pthead_mutex_t muetx = pthread_mutex_initializer
動態初始化:區域性變數應採用動態初始化。e.g. pthread_mutex_init(&mutex, null)
pthread_mutex_destroy函式
銷毀乙個互斥鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_lock函式
加鎖。可理解為將mutex--(或-1)
int pthread_mutex_lock(pthread_mutex_t *mutex);
pthread_mutex_unlock函式
解鎖。可理解為將mutex ++(或+1)
int pthread_mutex_unlock(pthread_mutex_t *mutex);
pthread_mutex_trylock函式
嘗試加鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex);
lock與unlock:
lock嘗試加鎖,如果加鎖不成功,執行緒阻塞,阻塞到持有該互斥量的其他執行緒解鎖為止。
unlock主動解鎖函式,同時將阻塞在該鎖上的所有執行緒全部喚醒,至於哪個執行緒先被喚醒,取決於優先順序、排程演算法。預設:先阻塞、先喚醒。
例如:t1 t2 t3 t4 使用一把mutex鎖。t1加鎖成功,其他執行緒均阻塞,直至t1解鎖。t1解鎖後,t2 t3 t4均被喚醒,並自動再次嘗試加鎖。
可假想mutex鎖 init成功初值為1。 lock 功能是將mutex--。 unlock將mutex++
lock與trylock:
lock加鎖失敗會阻塞,等待鎖釋放。
trylock加鎖失敗直接返回錯誤號(如:ebusy),不阻塞。
看如下程式:該程式是非常典型的,由於共享、競爭而沒有加任何同步機制,導致產生於時間有關的錯誤,造成資料混亂:
#include #include #include void *tfn(void *arg)
return null;
}int main(void)
pthread_join(tid, null);
return 0;
}
修改該程式,使用mutex互斥鎖進行同步。
定義全域性互斥量,初始化init(&m, null)互斥量,新增對應的destroy
兩個執行緒while中,兩次printf前後,分別加lock和unlock
將unlock挪至第二個sleep後,發現交替現象很難出現。
執行緒在操作完共享資源後本應該立即解鎖,但修改後,執行緒抱著鎖睡眠。睡醒解鎖後又立即加鎖,這兩個庫函式本身不會阻塞。
所以在這兩行**之間失去cpu的概率很小。因此,另外乙個執行緒很難得到加鎖的機會
#include #include #include #include #include pthread_mutex_t mutex; //定義鎖
void *tfn(void *arg)
return null;
}int main(void)
pthread_cancel(tid);
pthread_join(tid, null);
pthread_mutex_destroy(&mutex);
return 0;
}/*執行緒之間共享資源stdout*/
main 中加flag = 5 將***在while中-- 這時,主線程輸出5次後試圖銷毀鎖,但子執行緒未將鎖釋放,無法完成。
main 中加pthread_cancel()將子執行緒取消。
#include #include #include #include #include pthread_mutex_t mutex;
void err_thread(int ret, char *str)
}void *tfn(void *arg)
return null;
}int main(void)
pthread_cancel(tid); // 將子執行緒殺死,子執行緒中自帶取消點
pthread_join(tid, null);
pthread_mutex_destroy(&mutex);
return 0; //main中的return可以將整個程序退出
}/*執行緒之間共享資源stdout*/
結論:在訪問共享資源前加鎖,訪問結束後立即解鎖。鎖的"粒度"應越小越好。 執行緒和並行程式設計之執行緒同步(四)
執行緒的目的是為了並行的執行 有時執行緒需要互相等待。例如,如果兩個執行緒同時寫乙個變數,執行結果是未定義的。因此對共享資源的需要進行 同步 處理。qt提供了低階別的 高階別的機制處理執行緒同步。上述同步類可以保證函式的執行緒安全。但是為了保證函式執行緒安全,會有效能的損耗,這也是為什麼qt部分函式...
多執行緒之執行緒同步
pulse lockobj 表示釋放當前被lock的lockobj,容許其他執行緒呼叫。相當於暫時掛起當前執行緒 wait lockobj 表示等待當前被其他執行緒占用的lockobj。下面的 將會交替執行兩個執行緒 class ticktock console.write tick monitor...
執行緒之同步通訊
執行緒之同步通訊 所謂執行緒之同步通訊,為了避免死鎖,讓執行緒在進入堵塞狀態時盡量釋放其鎖定的資源,以為其他的執行緒提供執行的機會 執行緒間的通訊 兩個或兩個以上的執行緒處理同乙個資源,處理的動作是不一樣的。這樣就需要將不同的動作 放到不同的run 方法中,run方法要封裝到單獨的類中。同步中使用 ...