• 线程同步(AutoResetEvent与ManualResetEvent)


    前言


    在我们编写多线程程序时,会遇到这样一个问题:在一个线程处理的过程中,需要等待另一个线程处理的结果才能继续往下执行。比如:有两个线程,一个用来接收Socket数据,另一个用来处理Socket数据,而处理Socket数据的那个线程需要在接收到Socket数据后才能处理运行,就要等待接收线程接收数据。那么处理线程如何等待,接收线程又如何通知处理线程呢?

    其中一个比较好的方式就是使用AutoResetEvent/ManualResetEvent

     

    1. AutoResetEvent/ManualResetEvent介绍


    AutoResetEvent/ManualResetEvent是.Net给我们提供的用于线程间同步的对象。都有三个主要的函数:

    WaitOne: 用于阻塞线程,等待接收到继续运行信号

    Set:          用于发送同步信号,通知正在等待的线程继续运行

    Reset:      重置终止状态

    初始化构造函数ResetEvent(bool initialState),当initialState为true时,默认为终止状态(即阻塞无效);当initialState为false时,默认为非终止状态

    关于ResetEvent的终止状态与非终止状态,大家刚开始看的时候会感到难以理解,我举个很形象的例子,大家就会明白了。

    我们都有上班刷卡进大楼的经历,这里可以把ResetEvent看作是那个闸门,只有刷卡才能进。

    WaitOne就是等待刷卡,Set就是刷卡,而Reset则是关闭闸门,这样就知道,当初始化的时候,initialState为true时,意思就是说,默认闸门是开的;initialState为false时,默认闸门是关的

    所以,如果要进入大楼,步骤就应该是这样的(假设当前闸门是关着的),

    等待刷卡(WaitOne) ->刷卡(Set)->通信->关闭闸门(Reset)

     

    2. AutoResetEvent/ManualResetEvent的区别


    其实,这两个类的字面上的意思已经告诉了我们他们间的区别,Reset既然是关闭闸门,那也就是说,Auto是自动关闭闸门,Manual是手动关闭闸门。

    AutoResetEvent:刷卡通过后,闸门自动关闭,然后等待下一次刷卡

    ManualResetEvent:刷卡通过后,闸门不会自动关闭,如果不手动关闭(调用Reset方法),等待刷卡无效,人也就不用刷卡就能直接通过

    所以WaitOne是否有效,能不能阻塞线程取决于是否调用了Reset方法。

    而构造函数中initialState为true时,表示闸门默认第一次是打开的

     

    3. AutoResetEvent/ManualResetEvent使用示例


    接下来,我们用一个简单的示例来看一下这两个类的实际效果。 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace ResetEventTest
    {
        class Program
        {
         //初始化时默认为非终止状态false static AutoResetEvent autoRE = new AutoResetEvent(false); static ManualResetEvent manualRE = new ManualResetEvent(false); static void Main(string[] args) { (new Thread(AutoMethod1)).Start(); (new Thread(AutoMethod2)).Start(); (new Thread(ManualMethod1)).Start(); (new Thread(ManualMethod2)).Start(); Console.ReadKey(); } static void AutoMethod1() { Console.WriteLine("wait print AutoMethod 1:"); autoRE.WaitOne(); Console.WriteLine("AutoMethod 1"); } static void AutoMethod2() { Console.WriteLine("wait print AutoMethod 2:"); autoRE.WaitOne(); Console.WriteLine("AutoMethod 2"); } static void ManualMethod1() { Console.WriteLine("wait print ManualMethod 1:"); manualRE.WaitOne(); Console.WriteLine("ManualMethod 1"); } static void ManualMethod2() { Console.WriteLine("wait print ManualMethod 2:"); manualRE.WaitOne(); Console.WriteLine("ManualMethod 2"); } } }

    分别对应AutoResetEvent和ManualResetEvent启动两个线程,全部进入等待,这时候输出为:

    我们发现线程全部阻塞,这说明初始化时false,就是设置为阻塞状态,那我们把初始化参数改为true试下

    static AutoResetEvent autoRE = new AutoResetEvent(true);
    static ManualResetEvent manualRE = new ManualResetEvent(true);
    

    结果如下:

    发现(注意输出顺序可能不同),Auto仅阻塞了一个,而Manual则两个都没有阻塞,全部输出了,实际上即是Auto放行一个后,立即自动调用了Reset方法,导致AutoMethod2在没有获取Set通行信号前无法继续运行;而Manual因为没有手动调用Reset方法,WaitOne形同虚设没有起任何作用。

    还使用之前的代码,在启动线程的时候添加Set代码,如下:

    static AutoResetEvent autoRE = new AutoResetEvent(false);
    static ManualResetEvent manualRE = new ManualResetEvent(false);
    
    static void Main(string[] args)
    {
        (new Thread(AutoMethod1)).Start();
        autoRE.Set();
        (new Thread(AutoMethod2)).Start();
    
        (new Thread(ManualMethod1)).Start();
        manualRE.Set();
        (new Thread(ManualMethod2)).Start();
    
        Console.ReadKey();
    }

    当AutoMethod1线程启动后,调用了Set方法,那么AutoMethod1线程就获得了继续运行的信号,这时候启动AutoMethod2线程,因为AutoMethod1线程运行后自动重置了状态,所以AutoMethod2就会阻塞;而ManualMethod1线程之后调用Set,因为没有手动调用Reset重置状态,那么ManualMethod2将不会阻塞,如下结果验证了我们的想法:

     

    如果想要ManualMethod2阻塞,那么只要在ManualMethod2中,获取到Set信号后,再调用Reset方法即可

    static void ManualMethod1()
    {
        Console.WriteLine("wait print ManualMethod 1:");
        manualRE.WaitOne();
        manualRE.Reset();
        Console.WriteLine("ManualMethod 1");
    }
    

    结果:

     

    4. 其他操作


    其实我们查看ResetEvent的继承关系会发现,实际上ResetEvent继承自抽象类WaitHandle,WaitHandle下的SignalAndWait,WaitAll与WaitAny方法提供了我们更多样的控制线程同步的方式,这个在以后会进一步探讨。

     

  • 相关阅读:
    robotframework学习
    mvc4 分离Controller 出现 未找到路径“/”的控制器或该控制器未实现 IController
    Cocos2d-x-3.0 Touch事件处理机制
    android微信分享要注意的地方
    驾校管理系统
    计算机存储单位换算
    C# 网络编程
    Android SQLite操作类--封装
    Android--数据库操作框架
    中英文混合字符串截取java
  • 原文地址:https://www.cnblogs.com/houkui/p/4219008.html
Copyright © 2020-2023  润新知