C#多線程編程中的鎖體系根本用法。本站提示廣大學習愛好者:(C#多線程編程中的鎖體系根本用法)文章只能為提供參考,不一定能成為您想要的結果。以下是C#多線程編程中的鎖體系根本用法正文
平凡在多線程開辟中,總防止不了線程同步。本篇就對net多線程中的鎖體系做個簡略描寫。
目次
一:lock、Monitor
1:基本。
2: 感化域。
3:字符串鎖。
4:monitor應用
二:mutex
三:Semaphore
四:總結
一:lock、Monitor
1:基本
Lock是Monitor語法糖簡化寫法。Lock在IL會生成Monitor。
//======Example 1=====
string obj = "helloworld";
lock (obj)
{
Console.WriteLine(obj);
}
//lock IL會編譯成以下寫法
bool isGetLock = false;
Monitor.Enter(obj, ref isGetLock);
try
{
Console.WriteLine(obj);
}
finally
{
if (isGetLock)
{
Monitor.Exit(obj);
}
}
isGetLock參數是Framework 4.0後新加的。 為了使法式在一切情形下都可以或許肯定,能否有需要釋放鎖。例: Monitor.Enter拿不到鎖
Monitor.Enter 是可以鎖值類型的。鎖時會裝箱成新對象,所以沒法做到線程同步。
2:感化域
一:Lock是只能在過程內鎖,不克不及跨過程。走的是混雜結構,先自旋再轉成內核結構。
二:關於對type類型的鎖。以下:
//======Example 2=====
new Thread(new ThreadStart(() => {
lock (typeof(int))
{
Thread.Sleep(10000);
Console.WriteLine("Thread1釋放");
}
})).Start();
Thread.Sleep(1000);
lock(typeof(int))
{
Console.WriteLine("Thread2釋放");
}
運轉成果以下:
我們在來看個例子。
//======Example 3=====
Console.WriteLine(DateTime.Now);
AppDomain appDomain1 = AppDomain.CreateDomain("AppDomain1");
LockTest Worker1 = (LockTest)appDomain1.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
"ConsoleApplication1.LockTest");
Worker1.Run();
AppDomain appDomain2 = AppDomain.CreateDomain("AppDomain2");
LockTest Worker2 = (LockTest)appDomain2.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName,
"ConsoleApplication1.LockTest");
Worker2.Run();
/// <summary>
/// 跨運用法式域界限或長途拜訪時須要繼續MarshalByRefObject
/// </summary>
public class LockTest : MarshalByRefObject
{
public void Run()
{
lock (typeof(int))
{
Thread.Sleep(10000);
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName + ": Thread 釋放," + DateTime.Now);
}
}
}
運轉成果以下:
第一個例子解釋,在同過程同域,分歧線程下,鎖type int,其實鎖的是統一個int對象。所以要慎用。
第二個例子,這裡就簡略說下。
A: CLR啟動時,會創立 體系域(System Domain)和同享域(Shared Domain), 默許法式域(Default AppDomain)。 體系域和同享域是單例的。法式域可以有多個,例子中我們應用AppDomain.CreateDomain辦法創立的。
B: 按正常來講,每一個法式域的代碼都是隔離,互不影響的。但關於一些基本類型來講,每一個法式域都從新加載一份,就顯得有點糟蹋,帶來額定的消耗壓力。聰慧的CLR會把一些根本類型Object, ValueType, Array, Enum, String, and Delegate等地點的法式集MSCorLib.dll,在CLR啟動進程中都邑加載到同享域。 每一個法式域都邑應用同享域的基本類型實例。
C: 而每一個法式域都有屬於本身的托管堆。托管堆中最主要的是GC heap和Loader heap。GC heap用於援用類型實例的存儲,性命周期治理和渣滓收受接管。Loader heap保留類型體系,如MethodTable,數據構造等,Loader heap性命周期不受GC治理,跟法式域卸載有關。
所以同享域中Loader heap MSCorLib.dll中的int實例會一向保存著,直到過程停止。單個法式域卸載也不受影響。感化域很年夜有無!!!
這時候第二個例子也很輕易懂得了。 鎖int實例是跨法式域的,MSCorLib中的基本類型都是如許。 極輕易形成逝世鎖,慎用。 而自界說類型則會加載到本身的法式域,不會影響他人。
3:字符串的鎖
我們都曉得鎖的目標,是為了多線程下值被損壞。也曉得string在c#是個特別對象,值是不變的,每次更改都是一個新對象值,這也是推舉stringbuilder緣由。如例:
//======Example 4=====
string str1 = "mushroom";
string str2 = "mushroom";
var result1 = object.ReferenceEquals(str1, str2);
var result2 = object.ReferenceEquals(str1, "mushroom");
Console.WriteLine(result1 + "-" + result2);
/* output
* True-True
*/
正式因為c#中字符串的這類特征,所以字符串是在多線程下是不會被修正的,只讀的。它存在於SystemDomain域中managed heap中的一個hash table中。Key為string自己,Value為string對象的地址。
當法式域須要一個string的時刻,CLR起首在這個Hashtable依據這個string的hash code試著找對應的Item。假如勝利找到,則直接把對應的援用前往,不然就在SystemDomain對應的managed heap中創立該 string,並參加到hash table中,並把援用前往。所以說字符串的性命周期是基於全部過程的,也是跨AppDomain。
4:monitor用法
引見下Wait,Pulse,PulseAll的用法。有正文,年夜家直接看代碼吧。
static string str = "mushroom";
static void Main(string[] args)
{
new Thread(() =>
{
bool isGetLock = false;
Monitor.Enter(str, ref isGetLock);
try
{
Console.WriteLine("Thread1第一次獲得鎖");
Thread.Sleep(5000);
Console.WriteLine("Thread1臨時釋放鎖,並期待其他線程釋放告訴旌旗燈號。");
Monitor.Wait(str);
Console.WriteLine("Thread1接到告訴,第二次獲得鎖。");
Thread.Sleep(1000);
}
finally
{
if (isGetLock)
{
Monitor.Exit(str);
Console.WriteLine("Thread1釋放鎖");
}
}
}).Start();
Thread.Sleep(1000);
new Thread(() =>
{
bool isGetLock = false;
Monitor.Enter(str, ref isGetLock); //一向期待中,直到其他釋放。
try
{
Console.WriteLine("Thread2取得鎖");
Thread.Sleep(5000);
Monitor.Pulse(str); //告訴隊列裡一個線程,轉變鎖狀況。 Pulseall 告訴一切的
Console.WriteLine("Thread2告訴其他線程,轉變狀況。");
Thread.Sleep(1000);
}
finally
{
if (isGetLock)
{
Monitor.Exit(str);
Console.WriteLine("Thread2釋放鎖");
}
}
}).Start();
Console.ReadLine();
二:mutex
lock是不克不及跨過程鎖的。 mutex感化和lock相似,然則它能跨過程鎖資本(走的是windows內核結構)。 我們來看個例子
static bool createNew = false;
//第一個參數 能否應具有互斥體的初始所屬權。即createNew true時,mutex默許取得處置旌旗燈號
//第二個是名字,第三個能否勝利。
public static Mutex mutex = new Mutex(true, "mushroom.mutex", out createNew);
static void Main(string[] args)
{
//======Example 5=====
if (createNew) //第一個創立勝利,這時候候曾經拿到鎖了。 無需再WaitOne了。必定要留意。
{
try
{
Run();
}
finally
{
mutex.ReleaseMutex(); //釋放以後鎖。
}
}
//WaitOne 函數感化是阻攔以後線程,直到拿到收到其他實例釋放的處置旌旗燈號。
//第一個參數是期待超不時間,第二個能否加入高低文同步域。
else if (mutex.WaitOne(10000,false))//
{
try
{
Run();
}
finally
{
mutex.ReleaseMutex();
}
}
else//假如沒有發明處置旌旗燈號
{
Console.WriteLine("曾經有實例了。");
Console.ReadLine();
}
}
static void Run()
{
Console.WriteLine("實例1");
Console.ReadLine();
}
我們次序起A B實例測試下。 A起首拿到鎖,輸入 實例1 。 B在期待, 假如10秒內A釋放,B拿到履行Run()。 超時後輸入 曾經有實例了。
這裡留意的是第一個拿隨處理旌旗燈號 的實例,曾經拿到鎖了。不須要再WaitOne。 不然報異常。
三:Semaphore
即旌旗燈號量,我們可以把它懂得為進級版的mutex。mutex對一個資本停止鎖,semaphore則是對多個資本停止加鎖。
semaphore是由windows內核保持一個int32變量的線程計數器,線程每挪用一次、計數器減1、釋放後對應加一, 超越的線程則列隊等待。
走的是內核結構,所以semaphore也是可以跨過程的。
static void Main(string[] args)
{
Console.WriteLine("預備處置隊列");
bool createNew = false;
SemaphoreSecurity ss = new SemaphoreSecurity(); //旌旗燈號量權限掌握
Semaphore semaphore = new Semaphore(2, 2, "mushroom.Semaphore", out createNew,null);
for (int i = 1; i <= 5; i++)
{
new Thread((arg) =>
{
semaphore.WaitOne();
Console.WriteLine(arg + "處置中");
Thread.Sleep(10000);
semaphore.Release(); //即semaphore.Release(1)
//semaphore.Release(5);可以釋放多個,但不克不及跨越最年夜值。假如最初釋放的總量跨越自己總量,也會報錯。 不建議應用
}).Start(i);
}
Console.ReadLine();
}
四:總結
mutex、Semaphore 須要由托管代碼轉本錢地用戶形式代碼、再轉換為當地內核代碼。
反之異樣,饒了一年夜圈,機能確定不會很好。所以僅在須要跨過程的場景才應用。