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

      

  • 相关阅读:
    Yahoo军规的学习
    从github上拉取代码速度慢的解决方案
    hosts文件介绍
    Windows系统下用nginx服务器部署页面
    前端开发面试题
    IDEA中maven无法拉下依赖问题的解决
    利用补丁永久破解IDEA
    OC项目中常用第三方库和框架介绍
    [暑假集训]开训复健练习赛 G
    [暑假集训]开训复健练习赛 D
  • 原文地址:https://www.cnblogs.com/hbsfgl/p/4896468.html
Copyright © 2011-2022 走看看