複製鏈接
請複製以下鏈接發送給好友

線程同步

鎖定
線程同步:即當有一個線程在對內存進行操作時,其他線程都不可以對這個內存地址進行操作,直到該線程完成操作, 其他線程才能對該內存地址進行操作,而其他線程又處於等待狀態,實現線程同步的方法有很多,臨界區對象就是其中一種。
中文名
線程同步
外文名
thread synchronization
類    型
理論
應    用
物理
定    義
協同步調,按預定的先後次序進行

線程同步簡介

在一般情況下,創建一個線程是不能提高程序的執行效率的,所以要創建多個線程。但是多個線程同時運行的時候可能調用線程函數,在多個線程同時對同一個內存地址進行寫入,由於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] 

線程同步作用

線程有可能和其他線程共享一些資源,比如,內存,文件,數據庫等。
當多個線程同時讀寫同一份共享資源的時候,可能會引起衝突。這時候,我們需要引入線程“同步”機制,即各位線程之間要有個先來後到,不能一窩蜂擠上去搶作一團。
線程同步的真實意思和字面意思恰好相反。線程同步的真實意思,其實是“排隊”:幾個線程之間要排隊,一個一個對共享資源進行操作,而不是同時進行操作。 [2] 

線程同步方法

線程同步的方法:
(1)wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。
(2)sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉 InterruptedException異常。
(3)notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的 喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。
(4)notifyAll ():喚醒所有處於等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖, 而是讓它們競爭。 [3] 
參考資料
  • 1.    張雪松, 王鴻磊. MFC多線程同步類的使用[J]. 福建電腦, 2007(12):75-76.
  • 2.    甘羣文. C#多線程同步與異步的實現[J]. 電腦開發與應用, 2009, 22(9):35-37.
  • 3.    呂浩勇, 餘啓港, 董元和. Windows多線程同步技術研究[J]. 計算機與現代化, 2006(10):86-89.