zoukankan      html  css  js  c++  java
  • 5天不再惧怕多线程——第四天 信号量

    5天不再惧怕多线程——第四天 信号量

     

        今天整理“信号量”的相关知识,其实想想也蛮有趣的,锁,互斥,信号量都可以实现线程同步,在framework里面主要有三种。

    <1>:ManualResetEvent

    <2>:AutoResetEvent

    <3>: Semaphore

    好,下面就具体看看这些玩意的使用。

    一:ManualResetEvent

          该对象有两种信号量状态True和False,好奇的我们肯定想知道True和False有什么区别,稍后的例子见分晓,有三个方法值得学习一下。

    1:WaitOne

         该方法用于阻塞线程,默认是无限期的阻塞,有时我们并不想这样,而是采取超时阻塞的方法,如果超时就放弃阻塞,这样也就避免了无限期

           等待的尴尬。

    2:Set

         手动修改信号量为True,也就是恢复线程执行。

    3:ReSet

         手动修改信号量为False,暂停线程执行。

    好了,下面举个例子说明一下。

    <1>  信号量初始为False,WaitOne采用无限期阻塞,可以发现线程间可以进行交互。

    复制代码
     1 public class Example
    2 {
    3 public static void Main()
    4 {
    5 Thread t = new Thread(Run);
    6
    7 t.Name = "Jack";
    8
    9 Console.WriteLine("当前时间:{0} {1} {1},我是主线程,收到请回答。", DateTime.Now, t.Name);
    10
    11 t.Start();
    12
    13 Thread.Sleep(5000);
    14
    15 mr.Set();
    16
    17 Console.Read();
    18 }
    19
    20 static ManualResetEvent mr = new ManualResetEvent(false);
    21
    22 static void Run()
    23 {
    24 mr.WaitOne();
    25
    26 Console.WriteLine(" 当前时间:{0} 主线程,主线程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
    27 }
    28 }
    复制代码

    <2> 信号量初始为True,WaitOne采用无限期阻塞,实验发现WaitOne其实并没有被阻塞。

     static ManualResetEvent mr = new ManualResetEvent(true);

    <3>信号量初始为False,WaitOne采用超时2s,虽然主线程要等5s才能进行Set操作,但是WaitOne已经等不及提前执行了。

    复制代码
     1 public class Example
    2 {
    3 public static void Main()
    4 {
    5 Thread t = new Thread(Run);
    6
    7 t.Name = "Jack";
    8
    9 Console.WriteLine("当前时间:{0} {1} {1},我是主线程,收到请回答。", DateTime.Now, t.Name);
    10
    11 t.Start();
    12
    13 Thread.Sleep(5000);
    14
    15 mr.Set();
    16
    17 Console.Read();
    18 }
    19
    20 static ManualResetEvent mr = new ManualResetEvent(false);
    21
    22 static void Run()
    23 {
    24 mr.WaitOne(2000);
    25
    26 Console.WriteLine(" 当前时间:{0} 主线程,主线程,{1}已收到!", DateTime.Now, Thread.CurrentThread.Name);
    27 }
    28 }
    复制代码


    二:AutoResetEvent

          在VS对象浏览器中,我们发现AutoResetEvent和ManualResetEvent都是继承于EventWaitHandle,所以基本功能是一样的,不过值得注意

    的一个区别是WaitOne会改变信号量的值,比如说初始信号量为True,如果WaitOne超时信号量将自动变为False,而ManualResetEvent则不会。

    复制代码
     1 public class Example
    2 {
    3 public static void Main()
    4 {
    5 Thread t = new Thread(Run);
    6
    7 t.Name = "Jack";
    8
    9 t.Start();
    10
    11 Console.Read();
    12 }
    13
    14 static AutoResetEvent ar = new AutoResetEvent(true);
    15
    16 static void Run()
    17 {
    18 var state = ar.WaitOne(1000, true);
    19
    20 Console.WriteLine("我当前的信号量状态:{0}", state);
    21
    22 state = ar.WaitOne(1000, true);
    23
    24 Console.WriteLine("我恨你,不理我,您现在的状态是:{0}", state);
    25
    26 }
    27 }
    复制代码

    三:Semaphore 

         这玩意是.net 4.0新增的,用于控制线程的访问数量,默认的构造函数为initialCount和maximumCount,表示默认设置的信号量个数和

    最大信号量个数,其实说到底,里面是采用计数器来来分配信号量,当你WaitOne的时候,信号量自减,当Release的时候,信号量自增,然而

    当信号量为0的时候,后续的线程就不能拿到WaitOne了,所以必须等待先前的线程通过Release来释放。

    好了,下面还是举例子来说明一下:

    <1> initialCount=1,maximunCount=10,WaitOne采用无限期等待。

    复制代码
     1 namespace ConsoleApplication3
    2 {
    3 class Program
    4 {
    5 static void Main(string[] args)
    6 {
    7
    8 Thread t1 = new Thread(Run1);
    9 t1.Start();
    10
    11 Thread t2 = new Thread(Run2);
    12 t2.Start();
    13
    14 Console.Read();
    15 }
    16
    17 static Semaphore sem = new Semaphore(1, 10);
    18
    19 static void Run1()
    20 {
    21 sem.WaitOne();
    22
    23 Console.WriteLine("大家好,我是Run1");
    24 }
    25
    26 static void Run2()
    27 {
    28 sem.WaitOne();
    29
    30 Console.WriteLine("大家好,我是Run2");
    31 }
    32 }
    33 }
    复制代码

    我们悲剧的发现t2线程不能执行,我们知道WaitOne相当于自减信号量,然而默认的信号量个数为1,所以t2想执行必须等待t1通过Release来释放。

    复制代码
    1         static void Run1()
    2 {
    3 sem.WaitOne();
    4
    5 Console.WriteLine("大家好,我是Run1");
    6
    7 sem.Release();
    8 }
    复制代码

    可能有的同学要问,我不是设置了maximunCount=10吗?为什么没有起到作用?是的,默认情况下是没有起到作用,必须要我们手动干预一下,

    我们知道调用Release方法相当于自增一个信号量,然而Release有一个重载,可以指定自增到maximunCount个信号量,这里我就在主线程上

    Release(10),看看效果。

    复制代码
     1 namespace ConsoleApplication3
    2 {
    3 class Program
    4 {
    5 static void Main(string[] args)
    6 {
    7
    8 Thread t1 = new Thread(Run1);
    9 t1.Start();
    10
    11 Thread t2 = new Thread(Run2);
    12 t2.Start();
    13
    14 Thread.Sleep(1000);
    15
    16 sem.Release(10);
    17
    18 Console.Read();
    19 }
    20
    21 static Semaphore sem = new Semaphore(1, 10);
    22
    23 static void Run1()
    24 {
    25 sem.WaitOne();
    26
    27 Console.WriteLine("大家好,我是Run1");
    28 }
    29
    30 static void Run2()
    31 {
    32 sem.WaitOne();
    33
    34 Console.WriteLine("大家好,我是Run2");
    35 }
    36 }
    37 }
    复制代码


    <2> Semaphore命名,升级进程交互。

          在VS对象浏览器中发现Semaphore是继承字WaitHandle,而WaitHandle封装了win32的一些同步机制,所以当我们给Semaphore命名的时候

    就会在系统中可见,下面举个例子,把下面的代码copy一份,运行两个程序。

    复制代码
     1 namespace ConsoleApplication3
    2 {
    3 class Program
    4 {
    5 static void Main(string[] args)
    6 {
    7
    8 Thread t1 = new Thread(Run1);
    9 t1.Start();
    10
    11 Thread t2 = new Thread(Run2);
    12 t2.Start();
    13
    14 Console.Read();
    15 }
    16
    17 static Semaphore sem = new Semaphore(3, 10, "cnblogs");
    18
    19 static void Run1()
    20 {
    21 sem.WaitOne();
    22
    23 Console.WriteLine("当前时间:{0} 大家好,我是Run1", DateTime.Now);
    24 }
    25
    26 static void Run2()
    27 {
    28 sem.WaitOne();
    29
    30 Console.WriteLine("当前时间:{0} 大家好,我是Run2", DateTime.Now);
    31 }
    32 }
    33 }
    复制代码

    是的,我设置了信号量是3个,所以只能有三个线程持有WaitOne,后续的线程只能苦苦的等待。

     
  • 相关阅读:
    JDBC 复习4 批量执行SQL
    JDBC 复习3 存取Oracle大数据 clob blob
    Oracle复习
    Linux命令(1)grep
    JDBC 复习2 存取mysql 大数据
    JDBC 复习1 DBUtil
    php 环境搭建问题
    Windows 批处理 bat 开启 WiFi 菜单选项 设置ID PWD
    Bat 批处理启动和停止Oracle 服务
    docker 学习1 WSL docker ,Windows docker
  • 原文地址:https://www.cnblogs.com/zxtceq/p/5556447.html
Copyright © 2011-2022 走看看