• 锁机制


    1.Lock

        class Program
        {
            //资源
            static object obj = new object();
            static int count = 0;
    
            static void Main(string[] args)
            {
                for (int i = 0; i < 10; i++)
                {
                    lock(obj)
                    {
                        Thread t = new Thread(Run);
                        t.Start();
                    }
                }
                Console.Read();
            }
    
            static void Run()
            {
                Thread.Sleep(10);
                Console.WriteLine("当前数字:{0}", ++count);
            }
        }

    2.Monitor类

      这个算是实现锁机制的纯正类,在锁定的临界区中只允许让一个线程访问,其他线程排队等待。主要整理为2组方法。

    2.1Monitor.Enter和Monitor.Exit

      微软很照护我们,给了我们语法糖Lock,对的,语言糖确实减少了我们不必要的劳动并且让代码更可观,但是如果我们要精细的控制,则必须使用原生类,这里要注意一个问题就是“锁住什么”的问题,一般情况下我们锁住的都是静态对象,我们知道静态对象属于类级别,当有很多线程共同访问的时候,那个静态对象对多个线程来说是一个,不像实例字段会被认为是多个。

        class Program
        {
            //资源
            static object obj = new object();
            static int count = 0;
    
            static void Main(string[] args)
            {
                for (int i = 0; i < 10; i++)
                {
                    Thread t = new Thread(Run);
                    t.Start();
                }
                Console.Read();
            }
    
            static void Run()
            {
                Thread.Sleep(10);
                //进入临界区
                Monitor.Enter(obj);
                Console.WriteLine("当前数字:{0}", ++count);
                //退出临界区
                Monitor.Exit(obj);
            }
        }

    2.Monitor.Wait和Monitor.Pulse

    首先这两个方法是成对出现,通常使用在Enter,Exit之间。
    Wait:暂时的释放资源锁,然后该线程进入”等待队列“中,那么自然别的线程就能获取到资源锁。
    Pulse: 唤醒“等待队列”中的线程,那么当时被Wait的线程就重新获取到了锁。

    这里我们是否注意到了两点:
    1.可能A线程进入到临界区后,需要B线程做一些初始化操作,然后A线程继续干剩下的事情。
    2.用上面的两个方法,我们可以实现线程间的彼此通信。

        public class Program
        {
            public static void Main(string[] args)
            {
                LockObj obj = new LockObj();
    
                //注意,这里使用的是同一个资源对象obj
                Jack jack = new Jack(obj);
                John john = new John(obj);
    
                Thread t1 = new Thread(new ThreadStart(jack.Run));
                Thread t2 = new Thread(new ThreadStart(john.Run));
    
                t1.Start();
                t1.Name = "Jack";
    
                t2.Start();
                t2.Name = "John";
    
                Console.ReadLine();
            }
        }
    
        //锁定对象
        public class LockObj { }
    
        public class Jack
        {
            private LockObj obj;
    
            public Jack(LockObj obj)
            {
                this.obj = obj;
            }
    
            public void Run()
            {
                Monitor.Enter(this.obj);
    
                Console.WriteLine("{0}:111", Thread.CurrentThread.Name);
    
                //暂时的释放锁资源
                Monitor.Wait(this.obj);
    
                Console.WriteLine("{0}:222", Thread.CurrentThread.Name);
    
                //唤醒等待队列中的线程
                Monitor.Pulse(this.obj);
    
                Console.WriteLine("{0}:333", Thread.CurrentThread.Name);
    
                Monitor.Exit(this.obj);
            }
        }
    
        public class John
        {
            private LockObj obj;
    
            public John(LockObj obj)
            {
                this.obj = obj;
            }
    
            public void Run()
            {
                Monitor.Enter(this.obj);
    
                Console.WriteLine("{0}:111",Thread.CurrentThread.Name);
    
                //唤醒等待队列中的线程
                Monitor.Pulse(this.obj);
    
                Console.WriteLine("{0}:222", Thread.CurrentThread.Name);
    
                //暂时的释放锁资源
                Monitor.Wait(this.obj);
    
                Console.WriteLine("{0}:333", Thread.CurrentThread.Name);
    
                Monitor.Exit(this.obj);
            }
        }

    3.ReaderWriterLock类

      先前也知道,Monitor实现的是在读写两种情况的临界区中只可以让一个线程访问,那么如果业务中存在”读取密集型“操作,就好比数据库一样,读取的操作永远比写入的操作多。针对这种情况,我们使用Monitor的话很吃亏,不过没关系,ReadWriterLock就很牛X,因为实现了”写入串行“,”读取并行“。

    ReaderWriteLock中主要用3组方法:
    1.AcquireWriterLock: 获取写入锁。
    ReleaseWriterLock:释放写入锁。

    2.AcquireReaderLock: 获取读锁。
    ReleaseReaderLock:释放读锁。

    3.UpgradeToWriterLock:将读锁转为写锁。
    DowngradeFromWriterLock:将写锁还原为读锁。

        class Program
        {
            static List<int> list = new List<int>();
    
            static ReaderWriterLock rw = new System.Threading.ReaderWriterLock();
    
            static void Main(string[] args)
            {
                Thread t1 = new Thread(AutoAddFunc);
                Thread t2 = new Thread(AutoReadFunc);
                t1.Start();
                t2.Start();
                Console.Read();
            }
    
            /// <summary>
            /// 模拟3s插入一次
            /// </summary>
            /// <param name="num"></param>
            public static void AutoAddFunc()
            {
                //3000ms插入一次
                Timer timer1 = new Timer(new TimerCallback(Add), null, 0, 3000);
            }
    
            public static void AutoReadFunc()
            {
                //1000ms自动读取一次
                Timer timer1 = new Timer(new TimerCallback(Read), null, 0, 1000);
                Timer timer2 = new Timer(new TimerCallback(Read), null, 0, 1000);
                Timer timer3 = new Timer(new TimerCallback(Read), null, 0, 1000);
            }
    
            public static void Add(object obj)
            {
                var num = new Random().Next(0, 1000);
                //写锁
                rw.AcquireWriterLock(TimeSpan.FromSeconds(30));
                list.Add(num);
                Console.WriteLine("我是线程{0},我插入的数据是{1}。", Thread.CurrentThread.ManagedThreadId, num);
                //释放锁
                rw.ReleaseWriterLock();
            }
    
            public static void Read(object obj)
            {
                //读锁
                rw.AcquireReaderLock(TimeSpan.FromSeconds(30));
                Console.WriteLine("我是线程{0},我读取的集合为:{1}",
                Thread.CurrentThread.ManagedThreadId, string.Join(",", list));
                //释放锁
                rw.ReleaseReaderLock();
            }
        }

  • 相关阅读:
    关于异常处理解决
    多态
    类的继承和接口
    关于数组的应用知识
    String类型的字符串的知识点
    关于类的一些思想
    一些小程序的代码
    关于Java的一些基础了解
    将string类型的数字参数求和的小程序
    【【洛谷P2678 跳石头】——%%%ShawnZhou大佬】
  • 原文地址:https://www.cnblogs.com/lgxlsm/p/7511496.html
Copyright © 2020-2023  润新知