c# 進(jìn)程內(nèi)部的同步
在線程里,如果需要共享數(shù)據(jù),那么一定需要使用同步技術(shù),確保一次只有一個線程訪問和改變共享數(shù)據(jù)的狀態(tài)。在.net中,lock語句、Interlocked類和Monitor類可用于進(jìn)程內(nèi)部的同步。
1、lock語句與線程安全
lock語句是設(shè)置鎖定和解除鎖定的一種簡單方式。在使用lock語句之前,先進(jìn)入另一個爭用條件。例如:
public class SharedState
{
public int State { get; set; }
}
public class Job
{
SharedState sharedState;
public Job(SharedState sharedState)
{
this.sharedState = sharedState;
}
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
sharedState.State += 1;
}
}
}
static void Main()
{
int numTasks = 20;
var state = new SharedState();
var tasks = new Task[numTasks];//定義20個任務(wù)
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Run(() => new Job(state).DoTheJob());//啟動20個任務(wù),同時對數(shù)據(jù)進(jìn)行修改
}
for (int i = 0; i < numTasks; i++)
{
tasks[i].Wait();//等待所有任務(wù)結(jié)束
}
Console.WriteLine("summarized {0}", state.State);//預(yù)想應(yīng)該輸出:summarized 1000000
}
實際上的輸出與預(yù)想輸出并不一致,每次運行的輸出結(jié)果都不同,但沒有一個是正確的。如果將線程數(shù)量減少,那么得到正確值的次數(shù)會增多,但也不是每次都正確。
使用lock關(guān)鍵字,可以實現(xiàn)多個線程訪問同一個數(shù)據(jù)時的同步問題。lock語句表示等待指定對象的鎖定,該對象只能時引用類型。進(jìn)行鎖定后——只鎖定了一個線程,就運行l(wèi)ock語句塊中的代碼,在lock塊最后接觸鎖定,以便另一個線程可以鎖定該對象。
lock(obj)
{
//執(zhí)行代碼
}
//鎖定靜態(tài)成員,可以所以其類型(object)
lock(typeof(StaticCalss))
{
//執(zhí)行代碼
}
所以修改以上的代碼,使用SyncRoot模式。但是,如果是對屬性的訪問進(jìn)行鎖定:
public class SharedState
{
private object syncRoot = new object();
private int state = 0;
public int State
{
get { lock (syncRoot) return state; }
set { lock (syncRoot) state = value; }
}
}
仍會出現(xiàn)前面的爭用情況。在方法調(diào)用get存儲器,以獲得state的當(dāng)前值,然后set存儲器給state設(shè)置新值。在調(diào)用對象的get和set存儲器期間,對象并沒有鎖定,另一個線程仍然可以獲得臨時值。最好的方法是在不改變SharedState類的前提下,在調(diào)用方法中,將lock語句添加到合適的地方:
public class SharedState
{
public int State { get; set; }
}
public class Job
{
SharedState sharedState;
public Job(SharedState sharedState)
{
this.sharedState = sharedState;
}
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
lock (sharedState)
{
sharedState.State += 1;
}
}
}
}
在一個地方使用lock語句并不意味著訪問對象的其他線程都在等待。必須對每個訪問共享數(shù)據(jù)的線程顯示使用同步功能。
為使對state的修改作為一個原子操作,修改代碼:
public class SharedState
{
private int state = 0;
public int State { get { return state; } }
public int IncrementState()
{
lock (this)
{
return ++state;
}
}
}
//外部訪問
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
sharedState.IncrementState();
}
}
2、Interlocked類
Interlocked類用于使變量的簡單語句原子化。i++并非線程安全的,它涉及三個步驟:取值、自增、存值。這些操作可能被線程調(diào)度器打斷。Interlocked類提供了以線程安全的方式遞增、遞減、交換和讀取值的方法。Interlocked類只能用于簡單的同步問題,而且很快。因此,上面的IncrementState()方法的代碼可以改為:return Interlocked.Increment(ref state);
3、Monitor類
lcok語句最終會有C#編譯器解析為使用Monitor類。
lock(obj)
{
//執(zhí)行代碼
}
簡單的lock(obj)語句會被解析為調(diào)用Enter()方法,該方法會一直等待,直到線程鎖定對象。一次只有一個線程能鎖定對象,只要解除鎖定,線程就可以進(jìn)入同步階段。Monitor類的Exit()方法解除鎖定。編譯器把Exit()方法放在try塊的finally中,不論是否拋出異常,都將在語句塊運行末尾解除鎖定。
Monitor.Enter(obj);
try
{
//執(zhí)行代碼
}
finally
{
Monitor.Exit(obj);
}
相對于lock語句,Mpnitor類可以設(shè)置一個等待被鎖定的超時值。這樣就不會無限期的等待鎖定,如果等待鎖定時間超過規(guī)定時間,則返回false,表示未被鎖定,線程不再等待,執(zhí)行其他操作。也許以后,該線程會再次嘗試獲得鎖定:
bool lockTaken = false;
Monitor.TryEnter(obj,500, ref lockTaken);//在500ms內(nèi),是否鎖定了對象
if (lockTaken)
{
try
{
//執(zhí)行代碼
}
finally
{
Monitor.Exit(obj);
}
}
else
{
//未獲得鎖定,執(zhí)行代碼
}
如果基于對象的鎖定對象(Monitor)的系統(tǒng)開銷由于垃圾回收而過高,可以使用SpinLock結(jié)構(gòu)。,SpinLock結(jié)構(gòu)適用于:有大量的鎖定,而且鎖定時間總是非常短的情況。應(yīng)避免使用多個SpinLock結(jié)構(gòu),也不要調(diào)用任何可能阻塞的內(nèi)容。
以上就是c# 進(jìn)程內(nèi)部的同步的詳細(xì)內(nèi)容,更多關(guān)于c# 進(jìn)程同步的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解析從源碼分析常見的基于Array的數(shù)據(jù)結(jié)構(gòu)動態(tài)擴容機制的詳解
本篇文章是對從源碼分析常見的基于Array的數(shù)據(jù)結(jié)構(gòu)動態(tài)擴容機制進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C#如何使用PaddleOCR進(jìn)行圖片文字識別功能
PaddlePaddle(飛槳)是百度開發(fā)的深度學(xué)習(xí)平臺,旨在為開發(fā)者提供全面、靈活的工具集,用于構(gòu)建、訓(xùn)練和部署各種深度學(xué)習(xí)模型,它具有開放源代碼、高度靈活性、可擴展性和分布式訓(xùn)練等特點,這篇文章主要介紹了C#使用PaddleOCR進(jìn)行圖片文字識別,需要的朋友可以參考下2024-04-04

