多线程编程相对于单线程会出现一个特有的问题,就是线程安全的问题。所谓的线程安全,就是如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的。 线程安全问题都是由全局变量及静态变量引起的。
为了保证多线程情况下,访问静态变量的安全,可以用锁机制来保证,如下所示:
1 //需要加锁的静态全局变量 2 private static bool _isOK = false; 3 //lock只能锁定一个引用类型变量 4 private static object _lock = new object(); 5 static void MLock() 6 { 7 //多线程 8 new System.Threading.Thread(Done).Start(); 9 new System.Threading.Thread(Done).Start();10 Console.ReadLine();11 }12 13 static void Done()14 {15 //lock只能锁定一个引用类型变量16 lock (_lock)17 {18 if (!_isOK)19 {20 Console.WriteLine("OK");21 _isOK = true;22 }23 }24 }
需要注意的是,Lock只能锁住一个引用类型的对象。另外,除了锁机制外,高版本的C#中加入了async和await方法来保证线程安全,如下所示:
1 public static class AsynAndAwait 2 { 3 //step 1 4 private static int count = 0; 5 //用async和await保证多线程下静态变量count安全 6 public async static void M1() 7 { 8 //async and await将多个线程进行串行处理 9 //等到await之后的语句执行完成后 10 //才执行本线程的其他语句 11 //step 2 12 await Task.Run(new Action(M2)); 13 Console.WriteLine("Current Thread ID is {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 14 //step 6 15 count++; 16 //step 7 17 Console.WriteLine("M1 Step is {0}", count); 18 } 19 20 public static void M2() 21 { 22 Console.WriteLine("Current Thread ID is {0}", System.Threading.Thread.CurrentThread.ManagedThreadId); 23 //step 3 24 System.Threading.Thread.Sleep(3000); 25 //step 4 26 count++; 27 //step 5 28 Console.WriteLine("M2 Step is {0}", count); 29 } 30 }
在时序图中我们可以知道,共有两个线程进行交互,如下图所示:
用async和await后,上述代码的执行顺序为下图所示:
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时对一个变量执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。