閱讀目錄
一:臨界區概述
二:臨界區操作
三:通過lock關鍵字設定臨界區
四:通過Interlocked類設定臨界區
五:通過Monitor類設定臨界區
一:臨界區概述
在某個時間段內可以有兩個線程同時執行,那麼兩個線程訪問的要都是同一個資料的話,那麼這種情況下會出現一些不必要的事情,比如:我的銀行賬戶總額有5000,某天我拿著存摺去櫃檯取4000準備交物業費,媳婦拿著銀行卡去取2000給孩子交學費,當某一個時間點上,我倆同時取錢,那麼首先銀行是這樣操作的,先判斷賬戶餘額大於我要取的4000,好了給我吐出來4000大洋,因為是同時進行,媳婦那邊也是先判斷賬戶餘額大於她要取的2000,好了給她吐出來2000大洋,這樣的話我們賬戶總額只有5000元,我和我老婆卻一共取出來了6000元,那麼多吐出來的這1000銀行只能認了,銀行的賬戶餘額為-1000,我們是不希望這種情況發生的,那麼我們應該怎麼做呢?應該是這樣來操作的,首先當我取4000的時候,被鎖住也就是我們經常說的加鎖,同一個時間點上媳婦取2000的時候,取不了,等我取完了,鎖被釋放了,媳婦才能取,媳婦取2000的時候系統會提示餘額剩下1000了,不能提取2000,那麼這裡臨界區指的就是操作這個資料的地區
二:臨界區操作
. Lock關鍵字將某個語句塊標記為臨界區,另一個線程不能進入臨界區
. Interlocked類為多個線程共用的變數提供原子操作
. Monitor類提供同步對對象的訪問機制,Monitor類通過向單個對象設定對象鎖來控制對對象的訪問,使用Enter和Exit來標記臨界區的開頭和結尾
三:通過lock關鍵字設定臨界區
當我們取消Lock關鍵字的時候,報異常了一,是因為 _money為負數了,也就是賬戶現有金額為負數了,銀行的錢為負數了,我們不取消Lock關鍵字二程式運行正常,為什麼會這樣呢?紅色代碼和黃色代碼部分為原子操作,當有線程在訪問紅色代碼部分的時候,因為是並存執行,另外的線程也進來了減錢了,如果沒有lock關鍵字的存在,那麼兩個線程都能執行到黃色代碼部分,比如說this._money被減少到等於500的時候,因為intAmount一直都是500,那麼if(this._money >= intAmount)是成立的,那麼這兩個線程都能進到if的語句塊裡面做黃色代碼部分了也就是this._money = this._money - intAmount,第一個線程0=500-500,第二個線程-500=0-500,連著減兩次,所以最後this._money為負數了,所以我們要加lock關鍵字在一個時間點上只能一個線程進來
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace CriticalZone
{
public class Money
{
public int _money;//賬戶現有金額
/// <summary>
/// 初始化賬戶總額
/// </summary>
/// <param name="intTotalMoney">賬戶總額</param>
public Money(int intTotalMoney)
{
this._money = intTotalMoney;
}
/// <summary>
/// 判斷銀行剩下的錢是不是負數,如果是負數則拋出異常,如果不是負數則繼續取錢
/// </summary>
/// <param name="intAmount">要取走的金額</param>
/// <returns></returns>
public int Withdraw(int intAmount)
{
if (this._money < 0)
{
throw new Exception("銀行的錢為負數了");
}
return UseLock(intAmount);
}
public int UseLock(int intAmount)
{
//可以注釋lock看看效果
lock (this)
{
//如果銀行的錢大於等於要取走的金額,則減少錢
if (this._money >= intAmount)
{
Thread.Sleep(5);
this._money = this._money - intAmount;
return intAmount;
}
else
{
return 0;
}
}
}
/// <summary>
/// 取錢動作
/// </summary>
public void GetMoney()
{
//連著取100次500元現金,那麼就目前來說,賬戶總額為5000元,只能取十次就不能取了,賬戶為0了,金額就不應該在減少了
for (int i = 0; i < 100; i++)
{
Withdraw(500);
}
}
}
class Program
{
//聲明十個線程,相當於十個人同時去取錢
static Thread[] threads = new Thread[10];
static void Main(string[] args)
{
Money money = new Money(5000);
//建立十個線程,這十個線程做同樣一件事情GetMoney取錢
for(int i = 0;i < 10;i++)
{
Thread thread = new Thread(new ThreadStart(money.GetMoney));
threads[i] = thread;
}
//同時運行十個線程
for (int i = 0; i < 10; i++)
{
//開始執行GetMoney()
threads[i].Start();
}
Console.WriteLine("線程已經啟動");
Console.ReadLine();
}
}
}
圖一
圖二
四:通過Interlocked類設定臨界區
五:通過Monitor類設定臨界區