并行开发-线程同步机制

线程同步技术

争用条件:

如果两个或多个线程访问相同的对象,并且对共享状态的访问没有同步,就会出现争用条件

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) 可能会有问题,因为不受控制的代码也可能会锁定该对象

 

posted @ 2019-04-05 14:37  哪个昵称没被用  阅读(187)  评论(0编辑  收藏  举报