做爲C#的程序員來講,在遇到線程同步的需求時最經常使用的就是lock關鍵字。但如何正確並有效地使用lock,倒是可否高效地達到同步要求的關鍵。正由於如此,程序員須要徹底理解lock究竟爲程序作了什麼。程序員
所涉及的知識點安全
• lock的等效代碼多線程
• System.Threading.Monitor類型的做用和使用方法測試
分析問題線程
1.lock的等效代碼設計
在.NET的多線程程序中,常常會遇到lock關鍵字來控制同步,好比下列代碼:對象
private object o = new object();索引
public void Work()rem
{同步
lock(o)
{
//作一些須要線程同步的工做
}
}
事實上,lock這個關鍵字是C#爲方便程序員而定義的語法,它等效於安全地使用System.Threading.Monitor類型。上面的代碼就直接等效於下面的代碼:
private object o = new object();
public void Work()
{
//這裏很重要,是爲了不直接使用私有成員o,而致使線程不安全
object temp = o;
System.Threading.Monitor.Enter(temp);
try
{
//作一些須要線程同步的工做
}
finally
{
System.Threading.Monitor.Exit(temp);
}
}
正如你看到的,真正實現了線程同步功能的,就是System.Threading.Monitor類型,lock關鍵字只是用來代替調用Enter、Exit方法,而且將全部的工做包含在try塊內,以保證其最終退出同步。
2.System.Threading.Monitor類型的做用和使用
在前文中已經提到了,Monitor類型的Enter和Exit方法用來實現進入和退出對象的同步。具體來講,當Enter方法被調用時,對象的同步索引將被檢查,而且.NET將負責一系列的後續工做,來保證對象訪問時線程的同步,而Exit方法的調用,則保證了當前線程釋放該對象的同步塊。
示例
下列演示了自定義的類如何利用lock關鍵字(也就是Monitor類型)來實現線程同步,具體定義了一個包含須要同步執行方法的類型。
/// <summary>
/// 演示同步鎖
/// </summary>
public class MyLock
{
//用來在靜態方法中同步
private static object o1 = new object();
//用來在成員方法中不一樣
private object o2 = new object();
//成員變量
private static int i1 = 0;
private int i2 = 0;
/// <summary>
/// 測試靜態方法的同步
/// </summary>
/// <param name=" handleObject ">同步時操做的對象</param>
public static void Increment1(object handleObject)
{
lock (o1)
{
Console.WriteLine("i1的值爲:{0}", i1);
//這裏刻意製造線程並行機會,來檢查同步的功能
Thread.Sleep(200);
i1++;
Console.WriteLine("i1自增後爲:{0}", i1);
}
}
/// <summary>
/// 測試成員方法的同步
/// </summary>
/// <param name=" handleObject ">同步時操做的對象</param>
public void Increment2(object handleObject)
{
lock (o2)
{
Console.WriteLine("i2的值爲:{0}", i2);
//這裏刻意製造線程並行機會,來檢查同步的功能
Thread.Sleep(200);
i2++;
Console.WriteLine("i2自增後爲:{0}", i2);
}
}
}
這樣在main方法中,調用該類型對象的方法和其靜態方法,測試其同步的效果。代碼以下
/// <summary>
/// 程序入口
/// </summary>
class Program
{
/// <summary>
/// 測試同步效果
/// </summary>
static void Main(string[] args)
{
//開始多線程
Console.WriteLine("開始測試靜態方法的同步");
for (int i = 0; i < 5; i++)
{
Task t = new Task(MyLock.Increment1, i);
t.Start();
}
//這裏等待線程執行結束
Thread.Sleep(3 * 1000);
Console.WriteLine("開始測試成員方法的同步");
MyLock myLock = new MyLock();
//開始多線程
for (int i = 0; i < 5; i++)
{
Thread t = new Thread(myLock.Increment2);
t.Start();
}
Console.Read();
}
}
下面是程序的執行結果:
開始測試靜態方法的同步
i1的值爲:0
i1自增後爲:1
i1的值爲:1
i1自增後爲:2
i1的值爲:2
i1自增後爲:3
i1的值爲:3
i1自增後爲:4
i1的值爲:4
i1自增後爲:5
開始測試成員方法的同步
i2的值爲:0
i2自增後爲:1
i2的值爲:1
i2自增後爲:2
i2的值爲:2
i2自增後爲:3
i2的值爲:3
i2自增後爲:4
i2的值爲:4
i2自增後爲:5
總結
能夠看到,線程同步被很好地保證了。這裏須要強調的是,線程同步自己違反了多線程並行運行的原則,因此讀者在使用線程同步時應該儘可能作到把lock加在最小的程序塊上。若是一個方法有大量的代碼須要線程同步,那就須要從新考慮程序的設計了,是否真的有必要進行多線程處理,畢竟線程自己的開銷也是至關大的。
對靜態方法的同步,通常採用靜態私有的引用成員,而對成員方法的同步,通常採用私有的引用成員。讀者須要注意靜態和非靜態成員的使用,都把同步對象申明爲私有,這是保證線程同步高效而且正確的關鍵點。
說明:以上內容是根據網上內容進行整理。