在C#中使用lock、Monitor.Enter、Monitor.TryEnter的筆記
使用情境
例如常遇到的資料庫要取最後一筆編號+1當流水號,在多人同時使用系統的情況下
容易發生流水號重複的問題,
使用lock
class Program
{
public static object lockobj = new object();
static void Main(string[] args)
{
Program p = new Program();
var tt1 = new ParameterizedThreadStart(p.DoWrite);
Thread t1 = new Thread(tt1);
var tt2 = new ParameterizedThreadStart(p.DoWrite);
Thread t2 = new Thread(tt2);
t2.Start(2);
t1.Start(1);
}
public void DoWrite(object o)
{
lock(lockobj)
{
Console.WriteLine("start-"+o);
Thread.Sleep(3000);
Console.WriteLine("done-" + o);
}
}
}
執行結果:
因為先執行t2.Start(2); 再執行t1.Start(1); lock的情況下會先執行的跑完 才會執行下一個
如果把lock拿掉的話
public void DoWrite(object o)
{
//lock(lockobj)
//{
Console.WriteLine("start-"+o);
Thread.Sleep(3000);
Console.WriteLine("done-" + o);
//}
}
執行結果就會變成
使用Monitor.Enter
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var tt3 = new ParameterizedThreadStart(p.MDoWrite);
Thread t3 = new Thread(tt3);
t3.Start(3);
var tt4 = new ParameterizedThreadStart(p.MDoWrite);
Thread t4 = new Thread(tt4);
t4.Start(4);
}
public void MDoWrite(object o)
{
Monitor.Enter(this);
try
{
Console.WriteLine("start-" + o);
Thread.Sleep(3000);
}
finally
{
Console.WriteLine("done-" + o);
Monitor.Exit(this);
}
}
}
執行結果:
使用Monitor.TryEnter
在使用lock時要小心鎖死的問題
Monitor提供了TryEnter方法可以傳入等待逾時時間
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var tt5 = new ParameterizedThreadStart(p.M2DoWrite);
Thread t5 = new Thread(tt5);
t5.Start(5);
var tt6 = new ParameterizedThreadStart(p.M2DoWrite);
Thread t6 = new Thread(tt5);
t6.Start(6);
}
public void M2DoWrite(object o)
{
Monitor.TryEnter(this,6000);
try
{
Console.WriteLine("start-" + o);
Thread.Sleep(5000);
Console.WriteLine(o+"-完成" );
}
finally
{
Console.WriteLine("done-" + o);
Monitor.Exit(this);
}
Console.WriteLine("要跳出了");
}
}
執行結果:
假設超過傳入的等待逾時時間
public void M2DoWrite(object o)
{
Monitor.TryEnter(this,2000);
try
{
Console.WriteLine("start-" + o);
Thread.Sleep(5000);
Console.WriteLine(o+"-完成" );
}
finally
{
Console.WriteLine("done-" + o);
Monitor.Exit(this);
}
Console.WriteLine("要跳出了");
}
會發現t5 還沒結束 t6就Start了
而且還會吐Error
接著我把程式修改成這樣
public void M2DoWrite(object o)
{
bool NotOverTime = Monitor.TryEnter(this,2000);
try
{
Console.WriteLine("進入時間第{0}秒",DateTime.Now.Second);
Console.WriteLine("start-" + o);
Thread.Sleep(5000);
Console.WriteLine(o + "-完成");
}
finally
{
Console.WriteLine("done-" + o);
if (NotOverTime)
{
Monitor.Exit(this);
}
}
Console.WriteLine(o + "-要跳出了");
}
執行結果: