上一篇博客學習瞭如何簡單的使用多線程。其實普通的多線程確實很簡單,可是一個安全的高效的多線程卻不那麼簡單。因此不少時候不正確的使用多線程反倒會影響程序的性能。html
下面先看一個例子 :安全
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。以下:spa
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()); } } }
執行結果:pwa
lock 是一種比較好用的簡單的線程同步方式,它是經過爲給定對象獲取互斥鎖來實現同步的。能夠看到這種方式的確沒有阻塞主線程,並且成員變量的值也是連續遞增的,說明是線程安全的。lock 鎖機制表示在同一時刻只有一個線程能夠鎖定同步對象(在這裏是locker),任何競爭的的其它線程都將被阻止,直到這個鎖被釋放。線程
lock 的參數必須是基於引用類型的對象,不要是基本類型,好比 bool、int,這樣根本不能同步,緣由是lock的參數要求是對象,若是傳入 int,勢必要發生裝箱操做,這樣每次lock的都將是一個新的不一樣的對象。最好避免使用public類型或不受程序控制的對象實例,由於這樣極可能致使死鎖。永遠也不要 lock 一個字符串。code
暫時先到這裏,後面學了其餘方法在繼續更新。