线程同步技术
争用条件:
如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件
class StateObject { private int state = 5; public void ChangeState() { state++; if (state == 5) { Console.WriteLine("value=5"); } state = 5; } }
class ThreadTest { static void Main(string[] args) { StateObject m = new StateObject(); Thread t1 = new Thread(ChangeState); t1.Start(m); Console.ReadKey(); } static void ChangeState(object o) { StateObject m = o as StateObject; while (true) { m.ChangeState(); } } }
此时运行程序是没有输出的,因为StateObject类中state初始值是5,if条件不会进入,随后又将state从新初始化为5
两个线程执行
static void Main(string[] args) { StateObject m = new StateObject(); Thread t1 = new Thread(ChangeState); Thread t2 = new Thread(ChangeState); t1.Start(m); t2.Start(m); Console.ReadKey(); }
此时会不停的打印"value=5",原因在于:一个线程在判断语句处时,另一个线程可能又将state的值改为了5,而导致输出合法
死锁
class Deadlock{ static StateObject o1 = new StateObject(); static StateObject o2 = new StateObject(); public static void DeadlockA(object o) { lock (o1) { Console.WriteLine("我是线程{0},我锁定了对象o1", o); lock (o2) { Console.WriteLine("我是线程{0},我锁定了对象o2", o); } } } public static void DeadlockB(object o) { lock (o2) { Console.WriteLine("我是线程{0},我锁定了对象o2", o); lock (o1) { Console.WriteLine("我是线程{0},我锁定了对象o1", o); } } }}
Thread t1 = new Thread(Deadlock.DeadlockA); Thread t2 = new Thread(Deadlock.DeadlockB); t1.Start("t1"); t2.Start("t2");
t1线程执行DeadlockA()方法顺序锁定o1和o2,t2线程执行DeadlockB()方法顺序锁定o2和o1,当前结果显示t1线程锁定了o1后,t2线程在t1线程锁定o1后抢占进来,锁定了o2,t2在等t1解锁,t1在等t2解锁,都处于挂起状态在等对方解锁,这就形成了死锁,线程将无限等待下去
这个问题应该从一开始就设计好锁定顺序,也可以为锁定义超时时间来处理,保证“上锁”这个操作在一个线程上执行也是避免死锁的方法之一
C#中有多个用于多线程的同步技术
- lock语句
- Interlocked类
- Monitor类
- SpinLock类
- WaitHandle类
- Mutex类
- Semapphore类
- Event类
- Barrier类
- ReaderWriteLockSlim类
其中lock语句/Interlocked类/Monitor类可用于进程内存的同步,其它几个提供了多进程之间的线程同步
Lock
C#使用lock语句锁定在线程中共享的变量,如果一个线程锁定了变量,另一个线程就必须等待该锁定的解除
static void ChangeState(object o) { StateObject m = o as StateObject; while (true) { //给变量m加锁 lock (m) { m.ChangeState(); } } }
因为实例的对象也可以用于外部的同步访问,而且不能在类自身控制这种访问,所以应采用SyncRoot模式,创建私有对象,将这个对象用于lock语句
private static object syncRoot= new object(); static void ChangeState(object o) { StateObject m = o as StateObject; while (true) { lock (aync) { m.ChangeState(); } } }
需要注意:
1:lock只能锁定对象,即引用类型,不能锁定值类型.
2:lock不能锁定空值,因为null是不需要被释放的
3:不能锁定string类型,虽然它也是引用类型的。因为字符串类型被CLR“暂留”,这意味着整个程序中任何给定字符串都只有一个实例,具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例
4:避免锁定public类型,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象