zoukankan      html  css  js  c++  java
  • 第十二节:深究内核模式锁的使用场景(自动事件锁、手动事件锁、信号量、互斥锁、读写锁、动态锁)

    一. 整体介绍

    温馨提示:内核模式锁,在不到万不得已的情况下,不要使用它,因为代价太大了,有很多种替代方案。

      内核模式锁包括:

        ①:事件锁

        ②:信号量

        ③:互斥锁

        ④:读写锁

        ⑤:动态锁

    二. 事件锁

     事件锁包括:

    A. 自动事件锁(AutoResetEvent)

      使用场景:可以用此锁实现多线程环境下某个变量的自增.

      现实场景: 进站火车闸机,我们用火车票来实现进站操作.

      true: 表示终止状态,闸机中没有火车票

      false: 表示费终止状态,闸机中此时有一张火车票

    B.手动事件锁(ManualResetEvent)

      现实场景:有人看守的铁道栅栏(和自动事件锁不一样,不能混用)

      true: 栅栏没有合围,没有阻止行人通过铁路

      false:栅栏合围了, 阻止行人通过

    * 下面案例发现,锁不住,自增仍然是无序的输出了.

    * 核心方法:WaitOne和Set

     代码实践-自动事件锁:

     1  static AutoResetEvent autoResetLock1 = new AutoResetEvent(true);
     2  static AutoResetEvent autoResetLock2 = new AutoResetEvent(false);
     3  static int num2 = 0;
     4  {
     5                 //1. 能输出
     6                 {
     7                     autoResetLock1.WaitOne();
     8                     Console.WriteLine("autoResetLock1检验通过,可以通行");
     9                     autoResetLock1.Set();
    10                 }
    11 
    12                 //2. 不能输出
    13                 {
    14                     autoResetLock2.WaitOne();
    15                     Console.WriteLine("autoResetLock2检验通过,可以通行");
    16                     autoResetLock2.Set();
    17                 } 
    18 
    19                 //3.下面代码的结果:num从0-249,有序的发现可以锁住。
    20                 {
    21                     for (int i = 0; i < 5; i++)
    22                     {
    23                         Task.Factory.StartNew(() =>
    24                         {
    25                             for (int j = 0; j < 50; j++)
    26                             {
    27                                 try
    28                                 {
    29                                     autoResetLock1.WaitOne();
    30                                     Console.WriteLine(num2++);
    31                                     autoResetLock1.Set();
    32                                 }
    33                                 catch (Exception ex)
    34                                 {
    35                                     Console.WriteLine(ex.Message);
    36                                 }
    37 
    38                             }
    39                         });
    40                     }
    41                 }
    42             }

    代码实践-手动事件锁:

     1          static int num2 = 0;
     2          static ManualResetEvent mreLock = new ManualResetEvent(true);
     3          //下面代码锁不住,仍然是无序的输出了
     4                 {
     5                     for (int i = 0; i < 5; i++)
     6                     {
     7                         Task.Factory.StartNew(() =>
     8                         {
     9                             for (int j = 0; j < 50; j++)
    10                             {
    11                                 try
    12                                 {
    13                                     mreLock.WaitOne();
    14                                     Console.WriteLine(num2++);
    15                                     mreLock.Set();
    16                                 }
    17                                 catch (Exception ex)
    18                                 {
    19                                     Console.WriteLine(ex.Message);
    20                                 }
    21 
    22                             }
    23                         });
    24                     }
    25                 }

    三. 信号量

    信号量:

      * 核心类:Semaphore,通过int数值来控制线程个数。

      * 通过观察构造函数 public Semaphore(int initialCount, int maximumCount);:

      * initialCount: 可以同时授予的信号量的初始请求数。

      * maximumCount: 可以同时授予的信号量的最大请求数。

      * static Semaphore seLock = new Semaphore(1, 1); //表示只允许一个线程通过

    * 下面的案例可以有序的输出。

    * 核心方法:WaitOne和Release

     代码实践:

     1              static Semaphore seLock = new Semaphore(1, 1); //只允许一个线程通过 
    2 //下面代码锁住了,可以有序的输出 3 { 4 for (int i = 0; i < 5; i++) 5 { 6 Task.Factory.StartNew(() => 7 { 8 for (int j = 0; j < 50; j++) 9 { 10 try 11 { 12 seLock.WaitOne(); 13 Console.WriteLine(num2++); 14 seLock.Release(); 15 } 16 catch (Exception ex) 17 { 18 Console.WriteLine(ex.Message); 19 } 20 21 } 22 }); 23 } 24 }

    四. 互斥锁

    互斥锁:

      核心方法:WaitOne和ReleaseMutex

      下面案例可以锁住,有序输出

    总结以上三种类型的锁,都有一个WaitOne方法,观察源码可知,都继承于WaitHandle类。

     代码实践:

     1       static Mutex mutex = new Mutex();
     2         //下面代码锁住了,可以有序的输出
     3                 {
     4                     for (int i = 0; i < 5; i++)
     5                     {
     6                         Task.Factory.StartNew(() =>
     7                         {
     8                             for (int j = 0; j < 50; j++)
     9                             {
    10                                 try
    11                                 {
    12                                     mutex.WaitOne();
    13                                     Console.WriteLine(num2++);
    14                                     mutex.ReleaseMutex();
    15                                 }
    16                                 catch (Exception ex)
    17                                 {
    18                                     Console.WriteLine(ex.Message);
    19                                 }
    20 
    21                             }
    22                         });
    23                     }
    24                 }

    五. 读写锁

      读写锁(ReaderWriterLock):

      背景:多个线程读,一个线程写,如果写入的时间太久,此时读的线程会被卡死,这个时候就要用到读写锁了。

      锁读的两个核心方法:AcquireReaderLock和ReleaseReaderLock。

      锁写的两个核心方法:AcquireWriterLock和ReleaseWriterLock。

     代码实践:

     1        static ReaderWriterLock rwlock = new ReaderWriterLock();
     2         private void button24_Click(object sender, EventArgs e)
     3         {
     4             #region 01-读写锁
     5             {
     6                 //开启5个线程执行读操作
     7                 for (int i = 0; i < 5; i++)
     8                 {
     9                     Task.Run(() =>
    10                     {
    11                         Read();
    12                     });
    13                 }
    14                 //开启1个线程执行写操作
    15                 Task.Factory.StartNew(() =>
    16                 {
    17                     Write();
    18                 });
    19             }
    20             #endregion
    21 
    22         }
    23         /// <summary>
    24         /// 线程读
    25         /// </summary>
    26         static void Read()
    27         {
    28             while (true)
    29             {
    30                 Thread.Sleep(10);
    31                 rwlock.AcquireReaderLock(int.MaxValue);
    32                 Console.WriteLine("当前 t={0} 进行读取 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
    33                 rwlock.ReleaseReaderLock();
    34             }
    35         }
    36         /// <summary>
    37         /// 线程写
    38         /// </summary>
    39         static void Write()
    40         {
    41             while (true)
    42             {
    43                 Thread.Sleep(300);
    44                 rwlock.AcquireWriterLock(int.MaxValue);
    45                 Console.WriteLine("当前 t={0} 进行写入 {1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
    46                 rwlock.ReleaseWriterLock();
    47             }
    48         }

    六. 动态锁

    动态锁(CountdownEvent):

      * 作用:限制线程数的一个机制。

      * 业务场景:有Orders、Products、Users表,我们需要多个线程从某一张表中读取数据。

      * 比如:Order表10w,10个线程读取。(每个线程读1w)

           Product表5w,5个线程读取。(每个线程读1w)

             User表2w,2个线程读取。(每个线程读1w)

    三个核心方法:

      ①.Reset方法:重置当前的线程数量上限。(初始化的时候,默认设置一个上限)

      ②.Signal方法:将当前的线程数量执行减1操作。(使用一个thread,这个线程数量就会减1操作,直到为0后,继续下一步)

      ③.Wait方法:相当于我们的Task.WaitAll方法。

    代码实践:

     1  //初始化线程数量上限为10.
     2         static CountdownEvent cdLock = new CountdownEvent(10);
     3         private void button25_Click(object sender, EventArgs e)
     4         {
     5             //加载Orders搞定
     6             cdLock.Reset(10);
     7             for (int i = 0; i < 10; i++)
     8             {
     9                 Task.Factory.StartNew(() =>
    10                 {
    11                     LoadOrder();
    12                 });
    13             }
    14             cdLock.Wait();
    15             Console.WriteLine("所有的Orders都加载完毕。。。。。。。。。。。。。。。。。。。。。");
    16 
    17             //加载Product搞定
    18             cdLock.Reset(5);
    19             for (int i = 0; i < 5; i++)
    20             {
    21                 Task.Run(() =>
    22                 {
    23                     LoadProduct();
    24                 });
    25             }
    26             cdLock.Wait();
    27             Console.WriteLine("所有的Products都加载完毕。。。。。。。。。。。。。。。。。。。。。");
    28 
    29             //加载Users搞定
    30             cdLock.Reset(2);
    31             for (int i = 0; i < 2; i++)
    32             {
    33                 Task.Factory.StartNew(() =>
    34                 {
    35                     LoadUser();
    36                 });
    37             }
    38             cdLock.Wait();
    39             Console.WriteLine("所有的Users都加载完毕。。。。。。。。。。。。。。。。。。。。。");
    40 
    41             Console.WriteLine("所有的表数据都执行结束了。。。恭喜恭喜。。。。");
    42             Console.Read();
    43         }
    44         static void LoadOrder()
    45         {
    46             //书写具体的业务逻辑
    47             Console.WriteLine("当前LoadOrder正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
    48             //线程数量减1
    49             cdLock.Signal();
    50 
    51         }
    52         static void LoadProduct()
    53         {
    54             //书写具体的业务逻辑
    55             Console.WriteLine("当前LoadProduct正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
    56             //线程数量减1
    57             cdLock.Signal();
    58         }
    59         static void LoadUser()
    60         {
    61             //书写具体的业务逻辑
    62             Console.WriteLine("当前LoadUser正在加载中。。。{0}", Thread.CurrentThread.ManagedThreadId);
    63             //线程数量减1
    64             cdLock.Signal();
    65         }

  • 相关阅读:
    JAVAWEB进行PC支付宝支付
    SpringBoot 设置请求字符串格式为UTF-8
    SpringBoot 处理跨域请求问题
    Python 包制作
    F5 开发
    CentOS7 部署nfs服务
    Debian 9 部分快捷键失效问题
    Win10提示 该文件没有与之关联的程序来执行操作
    Debian 9 编译Python
    debian 9 安装远程桌面
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/8303045.html
Copyright © 2011-2022 走看看