• 使用线程以及对信号量 AutoResetEvent和ManualResetEvent的理解


    //声明线程  DoThreads是串口执行的方法名称
            Thread    _DoThreads = new Thread(new ThreadStart(DoThreads));
                _DoThreads.IsBackground = true; //是否跟着主线程结束而结束
                _DoThreads.Start(); //打开线程
    
    
    //在使用完线程后,使用这个方法关闭有线程,也可以自己单个关闭
                System.Environment.Exit(0);
    
    
    
    
      //使用线程打开方法,会自动关闭
                Task.Factory.StartNew(() =>
                {
                    start(_JieBoTaiPort);//方法名
                });

    上面是使用和声明线程的方法,但是在线程使用过程中还会有一个问题,就是两个线程,如果线程A改了公共变量C ,线程B想用线程A改过的C

    ,但是在线程运转中线程A和B,是同时运行的,所以还是会有情况下B获取不到A改的值的,

    所以就需要用到信号量了,先把线程B阻塞,如果C被改了,给个状态让他线程继续通行

    一、作用

    AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOneSetReset

    这三个方法的官方定义并不好理解,什么终止、非终止,乱七八糟的。在这里,我们以一种通俗易懂的概念来说明。

    二、比喻

    如果把每个线程比作一辆汽车的话,AutoResetEventManualResetEvent就是公路上的收费站。

    其中:

    Reset 关闭收费站车闸禁止通行(拦截车辆才好收费啊);

    WaitOne 收费员等待下一辆车辆过来(然后收费);

    Set    开启收费站车闸放行(交钱了就让过去)。

      

    三、AutoResetEvent和ManualResetEvent的区别

    既然AutoResetEventManualResetEvent都是收费站,那么它们之间有什么不同之处吗?

    顾名思义,Auto即自动,Manual即手动,而Reset根据上面的比喻表示关闭车闸,也就是前者可自动关闭车闸,后者需手动关闭车闸。

    自动关闭车闸:即一辆车交钱通过后,车闸会自动关闭,然后再等待下一辆车过来交费。即每辆车都要经过这么几个步骤:被阻 > 交费 > 通行 > 车闸关闭

    手动关闭车闸:车闸打开后,车闸不会自动关闭,如果不手动关闭车闸(即调用ManualResetEvent.Reset()方法)的话,车辆会一辆接一辆地通过

    所以WaitOne收费操作取决于车闸是否关闭(Reset),如果车闸是开启的,WaitOne的收费愿望只能落空,收费站形同虚设。

    四、AutoResetEvent和ManualResetEvent的初始状态

    通过设置AutoResetEventManualResetEvent构造函数可初始化收费站车闸状态:

    new Auto/ManualResetEvent(false)车闸默认关闭;
    new Auto/ManualResetEvent(true) 车闸默认开启。

    如果new Auto/ManualResetEvent(true),即车闸默认开启的话,WaitOne没任何意义,车辆该通过还通过。

      static EventWaitHandle _tollStation = new AutoResetEvent(true);//车闸默认开启
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//因车闸默认开启,WaitOne毫无意义,不会阻止车辆前行
                Console.WriteLine("噫!车闸是开的,我过来了!");
            }

    运行将打印:

    噫!车闸是开的,我过来了!

    如果将new AutoResetEvent(true) 改为new AutoResetEvent(flase),即车闸默认为关闭状态的话,将不会打印任何值,即车辆无法通过。

    那如何才能通过呢?必须在主线程中调用Set方法,即打开车闸即可通过。

    代码:

     static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();
                _tollStation.Set();//开启车闸
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//等待开启车闸,即_event.Set();
                Console.WriteLine("车闸开启,我过来了!");
            }

    运行将打印:

    车闸开启,我过来了!

    代码很明了,就不解释了,总之就是车闸默认关闭状态下,只有打开车闸(调用Set方法 ),车辆才能通行。

    五、用代码阐释AutoResetEvent的特性

    代码:

     static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();//车辆1
                new Thread(Car2).Start();//车辆2
                _tollStation.Set();
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
                Console.WriteLine("车辆1,顺利通过。");
            }
    
            static void Car2()
            {
                _tollStation.WaitOne();
                Console.WriteLine("车辆2,顺利通过。!");
            }

    运行将打印:

    车辆1,顺利通过。

    虽然车辆1和车辆2都在运行,但只有车辆1顺利通过。

    因为_tollStation.Set()仅运行了一次,即车辆1通过后车闸被立即关闭,导致车辆2未被通过。

    除非,在车辆1通过后再调用一次_tollStation.Set(),即再次打开车闸,车辆2才能通过:

    代码:

       static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();//车辆1
                new Thread(Car2).Start();//车辆2
                _tollStation.Set();//开启车闸,让车辆1通过
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
                Console.WriteLine("车辆1,顺利通过。");
                _tollStation.Set();//再开启一次车闸,让车辆2通过
            }
    
            static void Car2()
            {
                _tollStation.WaitOne();
                Console.WriteLine("车辆2,顺利通过。");
            }

    运行将打印:

    车辆1,顺利通过。

    车辆2,顺利通过。

    也就是每调用一次Set,仅有一个线程会继续。换言之,有多少个线程就要调用多少次Set,线程才会全部继续。

    这也表明,AutoResetEvent是典型的队列操作形式。

    六、用代码阐释ManualResetEvent的特性

    在上一个代码块中,_tollStation.Set()调用了两次,两辆车才顺利通过。

    那么,有没有什么办法,只调用一次_tollStation.Set()就让两辆或更多辆汽车顺利通过呢?

    答案是,将AutoResetEvent改为ManualResetEvent

     static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();//车辆1
                new Thread(Car2).Start();//车辆2
                _tollStation.Set();//开启车闸,所有车辆都会通过
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
                Console.WriteLine("车辆1,顺利通过。");
                //_tollStation.Set();//这里不再需要了
            }
    
            static void Car2()
            {
                _tollStation.WaitOne();
                Console.WriteLine("车辆2,顺利通过。");
            }

    运行将打印:

    车辆1,顺利通过。

    车辆2,顺利通过。

    这很好的说明了,ManualResetEvent开启车闸后不会自动关闭这一特性。所以调用一次_tollStation.Set(),全部车辆将顺利通过。

    如果在某一时刻手动关闭了车闸,则后面的车辆将无法通过。如以下代码:

    static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭
    
            static void Main(string[] args)
            {
                new Thread(Car1).Start();//车辆1
                new Thread(Car2).Start();//车辆2
    
                _tollStation.Set();//开启车闸,放行
                Timer timer = new Timer(CloseDoor, null, 0, 2000);//2秒后关闭车闸
    
                Console.ReadKey();
            }
    
            static void Car1()
            {
                _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
                Console.WriteLine("车辆1,顺利通过。");
            }
    
            static void Car2()
            {
                Thread.Sleep(3000);//睡眠3秒
                _tollStation.WaitOne();//当醒来后车闸已经被关闭
                Console.WriteLine("车辆2,顺利通过。");//所以车辆2不会被通过
            }
    
            /// <summary>
            /// 2秒后关闭车闸
            /// </summary>
            static void CloseDoor(object o)
            {
                _tollStation.Reset();//关闭车闸
            }

    运行将打印:

    车辆1,顺利通过。

    而车辆2将不会通过,因为当车辆2醒来时,车闸在2秒前已被关闭。

    七、总结

    1、看起来,ManualResetEvent更加自由、开放。如果把AutoResetEvent看作是只能单人通过的独木桥的话,那么ManualResetEvent就像一座城门,一下子可以涌入千军万马,当然你也可以随时关闭城门,让后面的人进不来。

    2、AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();

    3、如果共享资源仅允许一个线程单独使用的情况下,可以选择AutoResetEvent;如果共享资源允许多个线程同时使用,则可以选择ManualResetEvent

    4、如果要控制多个线程暂停、继续,可以选择ManualResetEvent

    此版本讲解仅供参考!

    这些也算是简单幽默的精讲非常适合小白去理解学习

    本文摘自https://blog.csdn.net/snlei/article/details/77651382

    感谢此博主那么费工夫的为大家提供那么简单易懂的示例

  • 相关阅读:
    reactnative遇到的问题总结
    swiper使用总结-坑点汇总
    echars配置案例-reactnative
    REST架构
    web万维网 -- 基础概念
    (四)值栈与OGNL
    (三)Struts2 拦截器
    (二)Struts2 核心知识
    (一)问候Struts2
    在eclipse中使用Maven3(笔记二)
  • 原文地址:https://www.cnblogs.com/wangshaod/p/11463585.html
Copyright © 2020-2023  润新知