zoukankan      html  css  js  c++  java
  • 线程锁机制

    当多个线程在并发的时候,难免会碰到相互冲突的事情,比如最经典的ATM机的问题,并发不可怕,可怕的是我们没有能力控制。

    线程以我的理解可以分为三种

    ① 锁。

    ② 互斥。

    ③ 信号。

      好,这一篇主要整理“锁”,C#提供了2种手工控制的锁

    一:  Monitor类

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

    1:Monitor.Enter和Monitor.Exit

             微软很照护我们,给了我们语法糖Lock,对的,语言糖确实减少了我们不必要的劳动并且让代码更可观,但是如果我们要精细的

         控制,则必须使用原生类,这里要注意一个问题就是“锁住什么”的问题,一般情况下我们锁住的都是静态对象,我们知道静态对象

         属于类级别,当有很多线程共同访问的时候,那个静态对象对多个线程来说是一个,不像实例字段会被认为是多个。

     1 class Program
     2     {
     3         static int count = 0;
     4 
     5         static void Main(string[] args)
     6         {
     7             for (int i = 0; i < 10; i++)
     8             {
     9                 Thread t = new Thread(Run);
    10 
    11                 t.Start();
    12             }
    13             Console.ReadLine();
    14         }
    15         static void Run()
    16         {
    17             Console.WriteLine("我是线程:" + Thread.CurrentThread.GetHashCode());
    18             Thread.Sleep(10);
    19 
    20             Console.WriteLine("当前数字:{0}", ++count);
    21         }
    22     }

    加锁的情况:

     1  class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             for (int i = 0; i < 10; i++)
     6             {
     7                 Thread t = new Thread(Run);
     8 
     9                 t.Start();
    10             }
    11             Console.ReadLine();
    12         }
    13 
    14         //资源
    15         static object obj = new object();
    16         static int count = 0;
    17 
    18         static void Run()
    19         {
    20             Thread.Sleep(10);
    21 
    22             //进入临界区
    23             Monitor.Enter(obj);
    24 
    25             Console.WriteLine("当前数字:{0}", ++count);
    26 
    27             //退出临界区
    28             Monitor.Exit(obj);
    29         }
    30     }

    2:Monitor.Wait和Monitor.Pulse

     首先这两个方法是成对出现,通常使用在Enter,Exit之间。

     Wait: 暂时的释放资源锁,然后该线程进入”等待队列“中,那么自然别的线程就能获取到资源锁。

     Pulse:  唤醒“等待队列”中的线程,那么当时被Wait的线程就重新获取到了锁。

    这里我们是否注意到了两点:

    ①   可能A线程进入到临界区后,需要B线程做一些初始化操作,然后A线程继续干剩下的事情。

    ②   用上面的两个方法,我们可以实现线程间的彼此通信。

     1 public class Program
     2     {
     3         public static void Main(string[] args)
     4         {
     5             LockObj obj = new LockObj();
     6 
     7             //注意,这里使用的是同一个资源对象obj
     8             Jack jack = new Jack(obj);
     9             John john = new John(obj);
    10 
    11             Thread t1 = new Thread(new ThreadStart(jack.Run));
    12             Thread t2 = new Thread(new ThreadStart(john.Run));
    13 
    14             t1.Start();
    15             t1.Name = "Jack";
    16 
    17             t2.Start();
    18             t2.Name = "John";
    19 
    20             Console.ReadLine();
    21         }
    22     }
    23 
    24     //锁定对象
    25     public class LockObj { }
    26 
    27     public class Jack
    28     {
    29         private LockObj obj;
    30 
    31         public Jack(LockObj obj)
    32         {
    33             this.obj = obj;
    34         }
    35 
    36         public void Run()
    37         {
    38             Monitor.Enter(this.obj);
    39 
    40             Console.WriteLine("{0}:我已进入茅厕。", Thread.CurrentThread.Name);
    41 
    42             Console.WriteLine("{0}:擦,太臭了,我还是撤!", Thread.CurrentThread.Name);
    43 
    44             //暂时的释放锁资源
    45             Monitor.Wait(this.obj);
    46 
    47             Console.WriteLine("{0}:兄弟说的对,我还是进去吧。", Thread.CurrentThread.Name);
    48 
    49             //唤醒等待队列中的线程
    50             Monitor.Pulse(this.obj);
    51 
    52             Console.WriteLine("{0}:拉完了,真舒服。", Thread.CurrentThread.Name);
    53 
    54             Monitor.Exit(this.obj);
    55         }
    56     }
    57 
    58     public class John
    59     {
    60         private LockObj obj;
    61 
    62         public John(LockObj obj)
    63         {
    64             this.obj = obj;
    65         }
    66 
    67         public void Run()
    68         {
    69             Monitor.Enter(this.obj);
    70 
    71             Console.WriteLine("{0}:直奔茅厕,兄弟,你还是进来吧,小心憋坏了!",
    72                                Thread.CurrentThread.Name);
    73 
    74             //唤醒等待队列中的线程
    75             Monitor.Pulse(this.obj);
    76 
    77             Console.WriteLine("{0}:哗啦啦....", Thread.CurrentThread.Name);
    78 
    79             //暂时的释放锁资源
    80             Monitor.Wait(this.obj);
    81 
    82             Console.WriteLine("{0}:拉完了,真舒服。", Thread.CurrentThread.Name);
    83 
    84             Monitor.Exit(this.obj);
    85         }
    86     }

    二: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);
            }
    
            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();
            }
        }
    

      

  • 相关阅读:
    每日日报2020.12.1
    每日日报2020.11.30
    981. Time Based Key-Value Store
    1146. Snapshot Array
    565. Array Nesting
    79. Word Search
    43. Multiply Strings
    Largest value of the expression
    1014. Best Sightseeing Pair
    562. Longest Line of Consecutive One in Matrix
  • 原文地址:https://www.cnblogs.com/hbsfgl/p/4896468.html
Copyright © 2011-2022 走看看