好記性不如爛筆頭,方便以后忘記了來這里看看,還有什么沒有提到的大家可以告訴我,幫忙完善,謝謝!~ Donate?[1]
1.2.1 lock關鍵字
使用lock關鍵字來確保當一個線程使用某些資源時,同時其他線程無法使用該資源.
class Counter
{
private readonly object _syncRoot = new object();
public int Count { get; private set; }
public void Add()
{
lock (_syncRoot)
{
Count++;
}
}
public void Sub()
{
lock (_syncRoot)
{
Count--;
}
}
}
var c = new Counter();
//創建多個線程來訪問c
Thread t1 = new Thread(()=> TestCounter(c));
Thread t2 = new Thread(() => TestCounter(c));
Thread t3 = new Thread(() => TestCounter(c));
Thread t4 = new Thread(() => TestCounter(c));
Thread t5 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t4.Start();
t5.Start();
t1.Join();
t2.Join();
t3.Join();
t4.Join();
t5.Join();
Console.WriteLine("count:"+c.Count);
最后輸出count:0,有人可能會說這不是很正常嗎,那請自己動手把Counter類中lock部分都移除再試一遍,最后輸出就會不一樣了!~
變成這樣:
public void Add()
{
Count++;
}
public void Sub()
{
Count--;
}
這是因為如果去掉lock部分,Counter類就不是線程安全的.
當多個線程同時訪問counter對象時,第一個線程得到的counter值是10并增加為11,
第二個線程得到的值是11并增加為12,
第一個線程得到counter值12,但遞減操作發生前,
第二個線程得到counter值也是12,
第一個線程將12遞減為11并存回counter中,
同時第二個線程進行了同樣的操作.
結果我們進行了兩次兩次遞增操作但只有第一遞減操作,
這顯然是不對的.
這種情形被稱為競爭條件(race condition),競爭條件是多線程環境中導致錯誤的常見原因.
為了確保不會發生以上情形,必須保證當有線程操作counter對象時,所有其他線程必須等待直到當前線程完成操作.我們可以使用lock關鍵字來實現這種行為.如果鎖定了一個對象,需要訪問該對象的所有其他線程則會處于阻塞狀態,并等待知道該對象解除鎖定,但這可能會導致嚴重的性能問題.
1.2.2 使用Monitor類鎖定資源
死鎖(deadlock)也是多線程編程中常見的錯誤.由于死鎖將導致程序停止工作,可以使用Monitor類來避免死鎖.
class Program
{
static void Main(string[] args)
{
object l1 = new object();
object l2 = new object();
new Thread(()=> LockObject(l1,l2)).Start();
lock (l2)
{
Console.WriteLine("Monitor.TryEnter 可以避免死鎖,在超時后返回false");
Thread.Sleep(1000);
if (Monitor.TryEnter(l1,TimeSpan.FromSeconds(5)))
{
Console.WriteLine("成功獲取到一個受保護的資源!");
}
else
{
Console.WriteLine("獲取資源超時!");
}
}
Console.WriteLine("------------------------------------");
new Thread(() => LockObject(l1, l2)).Start();
lock (l2)
{
Console.WriteLine("這將是一個死鎖!");
Thread.Sleep(1000);
lock (l1)
{
Console.WriteLine("成功獲取到一個受保護的資源!");
}
}
Console.WriteLine("End.");
Console.ReadKey();
}
static void LockObject(object l1,object l2)
{
lock (l1)
{
Thread.Sleep(1000);
lock (l2);
}
}
}
輸出(最后因為進入死鎖,所以End.未打印)
1.2.3 處理異常
在線程中始終使用try/catch代碼塊是非常重要的,因為不可能在線程代碼之外來捕獲異常.
class Program
{
static void Main(string[] args)
{
var t = new Thread(ThreadOne);
t.Start();
t.Join();
try
{
t = new Thread(ThreadTwo);
t.Start();
}
catch (Exception ex)
{
Console.WriteLine($"Main:{ex.Message}");
}
}
static void ThreadOne()
{
try
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(1000);
throw new Exception("Boom!!!");
}
catch (Exception ex)
{
Console.WriteLine($"ThreadOne:{ex.Message}");
}
}
static void ThreadTwo()
{
Console.WriteLine("Starting a faulty thread...");
Thread.Sleep(1000);
throw new Exception("Boom!!!");
}
}
輸出
由上圖可見ThreadOne的異常被捕獲,但是ThreadTwo的異常未被捕獲
-
贊賞支持 ?