c# 進(jìn)程內(nèi)部的同步
在線(xiàn)程里,如果需要共享數(shù)據(jù),那么一定需要使用同步技術(shù),確保一次只有一個(gè)線(xiàn)程訪問(wèn)和改變共享數(shù)據(jù)的狀態(tài)。在.net中,lock語(yǔ)句、Interlocked類(lèi)和Monitor類(lèi)可用于進(jìn)程內(nèi)部的同步。
1、lock語(yǔ)句與線(xiàn)程安全
lock語(yǔ)句是設(shè)置鎖定和解除鎖定的一種簡(jiǎn)單方式。在使用lock語(yǔ)句之前,先進(jìn)入另一個(gè)爭(zhēng)用條件。例如:
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個(gè)任務(wù)
for (int i = 0; i < numTasks; i++)
{
tasks[i] = Task.Run(() => new Job(state).DoTheJob());//啟動(dòng)20個(gè)任務(wù),同時(shí)對(duì)數(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
}
實(shí)際上的輸出與預(yù)想輸出并不一致,每次運(yùn)行的輸出結(jié)果都不同,但沒(méi)有一個(gè)是正確的。如果將線(xiàn)程數(shù)量減少,那么得到正確值的次數(shù)會(huì)增多,但也不是每次都正確。
使用lock關(guān)鍵字,可以實(shí)現(xiàn)多個(gè)線(xiàn)程訪問(wèn)同一個(gè)數(shù)據(jù)時(shí)的同步問(wèn)題。lock語(yǔ)句表示等待指定對(duì)象的鎖定,該對(duì)象只能時(shí)引用類(lèi)型。進(jìn)行鎖定后——只鎖定了一個(gè)線(xiàn)程,就運(yùn)行l(wèi)ock語(yǔ)句塊中的代碼,在lock塊最后接觸鎖定,以便另一個(gè)線(xiàn)程可以鎖定該對(duì)象。
lock(obj)
{
//執(zhí)行代碼
}
//鎖定靜態(tài)成員,可以所以其類(lèi)型(object)
lock(typeof(StaticCalss))
{
//執(zhí)行代碼
}
所以修改以上的代碼,使用SyncRoot模式。但是,如果是對(duì)屬性的訪問(wèn)進(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; }
}
}
仍會(huì)出現(xiàn)前面的爭(zhēng)用情況。在方法調(diào)用get存儲(chǔ)器,以獲得state的當(dāng)前值,然后set存儲(chǔ)器給state設(shè)置新值。在調(diào)用對(duì)象的get和set存儲(chǔ)器期間,對(duì)象并沒(méi)有鎖定,另一個(gè)線(xiàn)程仍然可以獲得臨時(shí)值。最好的方法是在不改變SharedState類(lèi)的前提下,在調(diào)用方法中,將lock語(yǔ)句添加到合適的地方:
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;
}
}
}
}
在一個(gè)地方使用lock語(yǔ)句并不意味著訪問(wèn)對(duì)象的其他線(xiàn)程都在等待。必須對(duì)每個(gè)訪問(wèn)共享數(shù)據(jù)的線(xiàn)程顯示使用同步功能。
為使對(duì)state的修改作為一個(gè)原子操作,修改代碼:
public class SharedState
{
private int state = 0;
public int State { get { return state; } }
public int IncrementState()
{
lock (this)
{
return ++state;
}
}
}
//外部訪問(wèn)
public void DoTheJob()
{
for (int i = 0; i < 50000; i++)
{
sharedState.IncrementState();
}
}
2、Interlocked類(lèi)
Interlocked類(lèi)用于使變量的簡(jiǎn)單語(yǔ)句原子化。i++并非線(xiàn)程安全的,它涉及三個(gè)步驟:取值、自增、存值。這些操作可能被線(xiàn)程調(diào)度器打斷。Interlocked類(lèi)提供了以線(xiàn)程安全的方式遞增、遞減、交換和讀取值的方法。Interlocked類(lèi)只能用于簡(jiǎn)單的同步問(wèn)題,而且很快。因此,上面的IncrementState()方法的代碼可以改為:return Interlocked.Increment(ref state);
3、Monitor類(lèi)
lcok語(yǔ)句最終會(huì)有C#編譯器解析為使用Monitor類(lèi)。
lock(obj)
{
//執(zhí)行代碼
}
簡(jiǎn)單的lock(obj)語(yǔ)句會(huì)被解析為調(diào)用Enter()方法,該方法會(huì)一直等待,直到線(xiàn)程鎖定對(duì)象。一次只有一個(gè)線(xiàn)程能鎖定對(duì)象,只要解除鎖定,線(xiàn)程就可以進(jìn)入同步階段。Monitor類(lèi)的Exit()方法解除鎖定。編譯器把Exit()方法放在try塊的finally中,不論是否拋出異常,都將在語(yǔ)句塊運(yùn)行末尾解除鎖定。
Monitor.Enter(obj);
try
{
//執(zhí)行代碼
}
finally
{
Monitor.Exit(obj);
}
相對(duì)于lock語(yǔ)句,Mpnitor類(lèi)可以設(shè)置一個(gè)等待被鎖定的超時(shí)值。這樣就不會(huì)無(wú)限期的等待鎖定,如果等待鎖定時(shí)間超過(guò)規(guī)定時(shí)間,則返回false,表示未被鎖定,線(xiàn)程不再等待,執(zhí)行其他操作。也許以后,該線(xiàn)程會(huì)再次嘗試獲得鎖定:
bool lockTaken = false;
Monitor.TryEnter(obj,500, ref lockTaken);//在500ms內(nèi),是否鎖定了對(duì)象
if (lockTaken)
{
try
{
//執(zhí)行代碼
}
finally
{
Monitor.Exit(obj);
}
}
else
{
//未獲得鎖定,執(zhí)行代碼
}
如果基于對(duì)象的鎖定對(duì)象(Monitor)的系統(tǒng)開(kāi)銷(xiāo)由于垃圾回收而過(guò)高,可以使用SpinLock結(jié)構(gòu)。,SpinLock結(jié)構(gòu)適用于:有大量的鎖定,而且鎖定時(shí)間總是非常短的情況。應(yīng)避免使用多個(gè)SpinLock結(jié)構(gòu),也不要調(diào)用任何可能阻塞的內(nèi)容。
以上就是c# 進(jìn)程內(nèi)部的同步的詳細(xì)內(nèi)容,更多關(guān)于c# 進(jìn)程同步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- c# 如何實(shí)現(xiàn)不同進(jìn)程之間的通信
- 如何使用C# 捕獲進(jìn)程輸出
- 如何利用c#實(shí)現(xiàn)通用守護(hù)進(jìn)程
- C# 獲取進(jìn)程退出代碼的實(shí)現(xiàn)示例
- C#獲取所有進(jìn)程的方法
- c#進(jìn)程之間對(duì)象傳遞方法
- C#中進(jìn)程的掛起與恢復(fù)
- C#實(shí)現(xiàn)啟動(dòng),關(guān)閉與查找進(jìn)程的方法
- C#實(shí)現(xiàn)強(qiáng)制關(guān)閉當(dāng)前程序進(jìn)程
- C#啟動(dòng)進(jìn)程的幾種常用方法
- C#遍歷系統(tǒng)進(jìn)程的方法
- C#操作進(jìn)程的方法介紹
相關(guān)文章
WinForm實(shí)現(xiàn)為T(mén)extBox設(shè)置水印文字功能
這篇文章主要介紹了WinForm實(shí)現(xiàn)為T(mén)extBox設(shè)置水印文字功能,很實(shí)用的一個(gè)技巧,需要的朋友可以參考下2014-08-08
C# WebApi 接口返回值不困惑:返回值類(lèi)型詳解
這篇文章主要介紹了C# WebApi 接口返回值不困惑:返回值類(lèi)型詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
C#實(shí)現(xiàn)多種圖片格式轉(zhuǎn)換的示例詳解
這篇文章主要為大家詳細(xì)介紹了C#如何實(shí)現(xiàn)多種圖片格式轉(zhuǎn)換,例如轉(zhuǎn)換成圖標(biāo)圖像ICO,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
C#難點(diǎn)逐個(gè)擊破(2):out返回參數(shù)
之前提到ref是將原方法中的參數(shù)影響的結(jié)果返回到調(diào)用它的方法中,out與ref類(lèi)似,相比之下,ref傳遞參數(shù)的地址,out是返回值。2010-02-02
Unity3D實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲(1)
這篇文章主要為大家詳細(xì)介紹了Unity3D實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-06-06
在C# WPF下自定義滾動(dòng)條ScrollViewer樣式的操作
這篇文章主要介紹了在C# WPF下自定義滾動(dòng)條ScrollViewer樣式的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
C#設(shè)置自定義文件圖標(biāo)實(shí)現(xiàn)雙擊啟動(dòng)(修改注冊(cè)表)
這篇文章介紹的是利用C#設(shè)置自定義文件圖標(biāo),然后實(shí)現(xiàn)雙擊啟動(dòng)的功能,文章給出了示例代碼,介紹的很詳細(xì),有需要的可以參考借鑒。2016-08-08

