上一篇博客學習瞭如何簡單的使用多線程。其實普通的多線程確實很簡單,但是一個安全的高效的多線程卻不那麼簡單。所以很多時候不正確的使用多線程反倒會影響程式的性能。 下麵先看一個例子 : 執行結果: 從上面可以看出變數 num 的值不是連續遞增的,輸出也是沒有順序的,而且每次輸出的值都是不一樣的,這是因為 ...
上一篇博客學習瞭如何簡單的使用多線程。其實普通的多線程確實很簡單,但是一個安全的高效的多線程卻不那麼簡單。所以很多時候不正確的使用多線程反倒會影響程式的性能。
下麵先看一個例子 :
class Program { static int num = 1; static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); //開始計時 stopWatch.Start(); ThreadStart threadStart = new ThreadStart(Run); for (int i = 0; i < 5; i++) { Thread thread = new Thread(threadStart); thread.Start(); } num++; Console.WriteLine("num is:" + num); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止計時 stopWatch.Stop(); //輸出執行的時間,毫秒數 Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds."); Console.ReadKey(); } public static void Run() { num++; Console.WriteLine("num is:" + num); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } }
執行結果:
從上面可以看出變數 num 的值不是連續遞增的,輸出也是沒有順序的,而且每次輸出的值都是不一樣的,這是因為非同步線程同時訪問一個成員時造成的,所以這樣的多線程對於我們來說是不可控的。以上這個例子就是非線程安全的,那麼要做到線程安全就需要用到線程同步。線程同步有很多種方法,比如之前用到過的 Join() 方法,它也可以實現線程的同步。下麵我們來試試:
class Program { static int num = 1; static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); //開始計時 stopWatch.Start(); ThreadStart threadStart = new ThreadStart(Run); for (int i = 0; i < 5; i++) { Thread thread = new Thread(threadStart); thread.Start(); thread.Join(); } num++; Console.WriteLine("num is:" + num); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止計時 stopWatch.Stop(); //輸出執行的時間,毫秒數 Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds."); Console.ReadKey(); } public static void Run() { num++; Console.WriteLine("num is:" + num); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } }
執行結果:
這樣就實現了簡單的同步,相比起上面的代碼也就只是添加了一行代碼(thread.Join();),之前也提到了 Join() 這個方法用於阻止當前線程,直到前面的線程執行完成。可是這樣雖然是實現了同步,但是卻也阻塞了主線程的繼續執行,這樣和單線程貌似沒什麼區別了。既然這樣我們再去學習一下其他的方法。
實現線程同步還有一種鎖的機制,下麵是一種最簡單的鎖機制,即使用 lock。如下:
class Program { private object locker = new object(); int num = 1; static void Main(string[] args) { Program program = new Program(); Stopwatch stopWatch = new Stopwatch(); //開始計時 stopWatch.Start(); ThreadStart threadStart = new ThreadStart(program.Run); for (int i = 0; i < 5; i++) { Thread thread = new Thread(threadStart); thread.Start(); } program.num++; Console.WriteLine("num is:" + program.num); Console.WriteLine("Main thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //停止計時 stopWatch.Stop(); //輸出執行的時間,毫秒數 Console.WriteLine("The execution time is " + stopWatch.ElapsedMilliseconds + " milliseconds."); Console.ReadKey(); } public void Run() { lock (locker) { num++; Console.WriteLine("num is:" + num); Console.WriteLine("Child thread ID is:" + Thread.CurrentThread.ManagedThreadId.ToString()); } } }
執行結果:
lock 是一種比較好用的簡單的線程同步方式,它是通過為給定對象獲取互斥鎖來實現同步的。可以看到這種方式的確沒有阻塞主線程,而且成員變數的值也是連續遞增的,說明是線程安全的。lock 鎖機製表示在同一時刻只有一個線程可以鎖定同步對象(在這裡是locker),任何競爭的的其它線程都將被阻止,直到這個鎖被釋放。
lock 的參數必須是基於引用類型的對象,不要是基本類型,比如 bool、int,這樣根本不能同步,原因是lock的參數要求是對象,如果傳入 int,勢必要發生裝箱操作,這樣每次lock的都將是一個新的不同的對象。最好避免使用public類型或不受程式控制的對象實例,因為這樣很可能導致死鎖。永遠也不要 lock 一個字元串。
暫時先到這裡,後面學了其他方法在繼續更新。