由一個(gè)簡單的例子來分析多線程的執(zhí)行,先看看簡單的例子,代碼如下:
class Program { static int count = 0; static void Main(string[] args) { for (int i = 0; i < 10; i++) { Thread t = new Thread(Run); t.Name = i.ToString(); t.Start(); } Console.Read(); } static void Run() { ++count; Console.WriteLine("當(dāng)前線程:{0},Count值為:{1}", Thread.CurrentThread.Name, count); }
程序在本機(jī)上的執(zhí)行結(jié)果,如上圖所示。如果電腦的Cpu比較好的,可能執(zhí)行的結(jié)果跟單線程差不多。
下面來分析下執(zhí)行結(jié)果:
for循環(huán)依次開了十個(gè)線程,然后調(diào)用了線程的Start方法。Start方法在MSDN的摘要為:導(dǎo)致操作系統(tǒng)將當(dāng)前實(shí)例的狀態(tài)更改為 System.Threading.ThreadState.Running。要注意的是調(diào)用線程的Start方法,并不代表線程能馬上啟動(dòng)起來(也許CPU正在忙其他的事情)。如何判斷線程真的執(zhí)行起來呢,只需要借助線程的IsAlive屬性。
接下來,只根據(jù)上面的執(zhí)行結(jié)果,看程序到底是如何跑的。 "線程0",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài),執(zhí)行Run()方法,靜態(tài)字段count加1,輸出當(dāng)前線程名稱,以及Count值。
"線程1",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài),執(zhí)行Run()方法,靜態(tài)字段count加1,執(zhí)行輸出,沒有完成輸出。
"線程2",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài)。執(zhí)行Run()方法,靜態(tài)字段count加1,立即輸出當(dāng)前線程名稱,以及Count值。
"線程1" 完成輸出。
"線程3",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài)。執(zhí)行Run()方法,靜態(tài)字段count加1,立即輸出當(dāng)前線程名稱,以及Count值。
"線程4",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài)。執(zhí)行Run()方法,靜態(tài)字段count加1,還未執(zhí)行輸出。
"線程5",啟動(dòng),線程立刻進(jìn)入執(zhí)行狀態(tài)。執(zhí)行Run()方法,靜態(tài)字段count加1,還未執(zhí)行輸出。
"線程4",完成執(zhí)行輸出。
"線程5",完成執(zhí)行輸出。
下面的結(jié)果就不分析了。 如何保證靜態(tài)字段Count加一后,完成輸出,下一個(gè)線程才能調(diào)用Run方法,也就是說Run方法,在當(dāng)前線程執(zhí)行完后,下一個(gè)線程才能執(zhí)行。在實(shí)際編程中,這是經(jīng)常會(huì)遇到的。 我知道的有兩個(gè)方法(都是對臨界資源進(jìn)行加鎖),第一個(gè)使用lock,第二個(gè)是借助于Monitor的Enter、Exit方法。兩個(gè)方法都需要一個(gè)靜態(tài)的Object對象,當(dāng)做臨界資源。代碼如下:
static object lockObj = new object(); lock (lockObj) { ++count; Console.WriteLine("當(dāng)前線程:{0},Count值為:{1}", Thread.CurrentThread.Name, count); }
static void Run() { Monitor.Enter(lockObj); ++count; Console.WriteLine("當(dāng)前線程:{0},Count值為:{1}", Thread.CurrentThread.Name, count); Monitor.Exit(lockObj); }