簡介
在一般情況下,創建一個執行緒是不能提高程式的執行效率的,所以要創建多個執行緒。但是多個線
程同時運行的時候可能調用執行緒函式,在多個執行緒同時對同一個記憶體地址進行寫入,由於CPU時間調度上的問題,寫入數據會被多次的覆蓋,所以就要使執行緒同步。
同步就是協同步調,按預定的先後次序進行運行。如:你說完,我再說。
“同”字從字面上容易理解為一起動作
其實不是,“同”字應是指協同、協助、互相配合。
如進程、執行緒同步,可理解為進程或執行緒A和B一塊配合,A執行到一定程度時要依靠B的某個結果,於是停下來,示意B運行;B依言執行,再將結果給A;A再繼續操作。
所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,該調用就不返回,同時其它執行緒也不能調用這個方法。按照這個定義,其實絕大多數函式都是同步調用(例如sin, isdigit等)。但是一般而言,我們在說同步、異步的時候,特指那些需要其他部件協作或者需要一定時間完成的任務。例如Window API函式SendMessage。該函式傳送一個訊息給某個視窗,在對方處理完訊息之前,這個函式不返回。當對方處理完畢以後,該函式才把訊息處理函式所返回的LRESULT值返回給調用者。
在多執行緒編程裡面,一些敏感數據不允許被多個執行緒同時訪問,此時就使用同步訪問技術,保證數據在任何時刻,最多有一個執行緒訪問,以保證數據的完整性。
舉例說明
在Java裡面,通過synchronized進行同步的保證。例如:
class MyTest{private static final Object lock=new Object();public static synchronized void test(){//同步的方法}public void test2(){synchronized(lock){//方法級同步,也可以使用this實現對象級同步}}}
在C++ 11裡面,通過std::mutex的加鎖和解鎖來保證。例如:
#include<mutex>#include<thread>usingnamespacestd;mutexm;voidthreadFunc(inti){m.lock();//在這裡寫上你需要的代碼m.unlock();}intmain(){threadt1(threadFunc,1);threadt2(threadFunc,2);t1.join();t2.join();return0;}
執行緒同步的方式和機制
臨界區(Critical Section)、互斥對象(Mutex):主要用於互斥控制;都具有擁有權的控制方法,只有擁有該對象的執行緒才能執行任務,所以擁有,執行完任務後一定要釋放該對象。
信號量(Semaphore)、事件對象(Event):事件對象是以通知的方式進行控制,主要用於同步控制!
1、臨界區:通過對多執行緒的串列化來訪問公共資源或一段代碼,速度快,適合控制數據訪問。在任意時刻只允許一個執行緒對共享資源進行訪問,如果有多個執行緒試圖訪問公共資源,那么在有一個執行緒進入後,其他試圖訪問公共資源的執行緒將被掛起,並一直等到進入臨界區的執行緒離開,臨界區在被釋放後,其他執行緒才可以搶占。它並不是核心對象,不是屬於作業系統維護的,而是屬於進程維護的。
總結下關鍵段:
1)關鍵段共初始化化、銷毀、進入和離開關鍵區域四個函式。
2)關鍵段可以解決執行緒的互斥問題,但因為具有“執行緒所有權”,所以無法解決同步問題。
3)推薦關鍵段與旋轉鎖配合使用。
2、互斥對象:互斥對象和臨界區很像,採用互斥對象機制,只有擁有互斥對象的執行緒才有訪問公共資源的許可權。因為互斥對象只有一個,所以能保證公共資源不會同時被多個執行緒同時訪問。當前擁有互斥對象的執行緒處理完任務後必須將執行緒交出,以便其他執行緒訪問該資源。
總結下互斥量Mutex:
1)互斥量是核心對象,它與關鍵段都有“執行緒所有權”所以不能用於執行緒的同步。
2)互斥量能夠用於多個進程之間執行緒互斥問題,並且能解決某進程意外終止所造成的“遺棄”問題。 3、信號量:信號量也是核心對象。它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目
在用CreateSemaphore()創建信號量時即要同時指出允許的最大資源計數和當前可用資源計數。一般是將當前可用資源計數設定為最 大資源計數,每增加一個執行緒對共享資源的訪問,當前可用資源計數就會減1 ,只要當前可用資源計數是大於0 的,就可以發出信號量信號。但是當前可用計數減小 到0 時則說明當前占用資源的執行緒數已經達到了所允許的最大數目,不能在允許其他執行緒的進入,此時的信號量信號將無法發出。執行緒在處理完共享資源後,應在離 開的同時通過ReleaseSemaphore ()函式將當前可用資源計數加1 。在任何時候當前可用資源計數決不可能大於最大資源計數。
4、事件對象: 通過通知操作的方式來保持執行緒的同步,還可以方便實現對多個執行緒的優先權比較的操作
總結下事件Event
1)事件是核心對象,事件分為手動置位事件和自動置位事件。事件Event內部它包含一個使用計數(所有核心對象都有),一個布爾值表示是手動置位事件還是自動置位事件,另一個布爾值用來表示事件有無觸發。
2)事件可以由SetEvent()來觸發,由ResetEvent()來設成未觸發。還可以由PulseEvent()來發出一個事件脈衝。
3)事件可以解決執行緒間同步問題,因此也能解決互斥問題。
作用
執行緒有可能和其他執行緒共享一些資源,比如,記憶體,檔案,資料庫等。
當多個執行緒同時讀寫同一份共享資源的時候,可能會引起衝突。這時候,我們需要引入執行緒“同步”機制,即各位執行緒之間要有個先來後到,不能一窩蜂擠上去搶作一團。
執行緒同步的真實意思和字面意思恰好相反。執行緒同步的真實意思,其實是“排隊”:幾個執行緒之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。
方法
執行緒同步的方法:
(1)wait():使一個執行緒處於等待狀態,並且釋放所持有的對象的lock。
(2)sleep():使一個正在運行的執行緒處於睡眠狀態,是一個靜態方法,調用此方法要捕捉 InterruptedException異常。
(3)notify():喚醒一個處於等待狀態的執行緒,注意的是在調用此方法的時候,並不能確切的 喚醒某一個等待狀態的執行緒,而是由JVM確定喚醒哪個執行緒,而且不是按優先權。
(4)notityAll ():喚醒所有處入等待狀態的執行緒,注意並不是給所有喚醒執行緒一個對象的鎖, 而是讓它們競爭。