C sharp中使用AutoResetEvent或者ManualResetEvent做线程管理

1. Task/thread/sync/async..await/WhenAll

相关基础知识参见此处链接

2. 什么是AutoResetEvent和ManualResetEvent事件

  • 他们都是C#中System.Threading下面的类。用于在多个线程之间进行事件通知和管理。
  • 他们的管理方法主要是三个:
    • Reset():关闭
    • WaitOne():阻挡
    • Set():启动
  • AutoResetEvent和ManualResetEvent的区别
    • 自动方法遵循set -> WaitOne -> Reset的流程自动执行,但是自动方法的一个set只能控制一个线程
    • 手动方法也是遵循set -> WaitOne -> Reset的流程,但是手动方法可以灵活的进行手动控制,一个手动的set可以控制多个同步线程。

3. 理解

  • 不管是auto还是manual,都要经历的步骤如下(其他地方看到一个很形象的举例):
    1
    2
    3
    4
    5
    6
    7
    8
    车辆过高速收费站时:
    自动:车闸默认关闭(new AutoResetEvent(false)) -> 车辆到达收费站被阻(waitone()) -> 交费,然后开闸(set()) -> 关闭车闸(reset())

    手动:车闸打开后(set()),并不会自动关闭,所以如果不去手动关闭(reset()),后面的车会一辆一辆的通过。

    自动可以自动关闭车闸(reset()),让一次只能过一个车,特点就是自动情况下,同步存在两个同步线程,最终只能执行第一个线程。
    手动需要手动关闭车闸(reset()),特点就是手动情况下,如果不手动reset(),所有线程都会被依次执行完毕。
    WaitOne()是否生效取决于有没有Reset()。如果一开始就是Reset()的,那么WaitOne不会阻挡任何线程执行。

4. AutoResetEvent和ManualResetEvent的初始化状态

  • new Auto/ManualResetEvent(false):默认阻挡线程。
  • new Auto/ManualResetEvent(true):默认通过线程。
  • new Auto/ManualResetEvent(true)中,WaitOne没有任何意义,它阻挡不了线程

5. auto/manual测试代码

  • autoresetevent中的验证代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    internal class Program
    {
    static EventWaitHandle my_task1 = new AutoResetEvent(true);//默认自动执行
    static AutoResetEvent my_task2 = new AutoResetEvent(false);//默认自动阻挡
    //static ManualResetEvent my_task3 = new ManualResetEvent(false);//默认手动阻挡
    private static void Main(string[] args)
    {
    //thread
    new Thread(func1).Start();

    new Thread(func3).Start();

    my_task2.Set();//开启,因为func3的调用在func2的上面,所以set先管理到func3。func3能执行,func2不能执行,这表明autoresetevent是典型的队列操作形式

    new Thread(func2).Start();

    Console.ReadKey();
    }
    static void func1()
    {
    my_task1.WaitOne();
    Console.WriteLine("此时的waitOne不能阻挡线程执行");
    }
    static void func2()
    {
    my_task2.WaitOne();
    Console.WriteLine("此时的waitOne会阻挡线程执行");
    }
    static void func3()
    {
    my_task2.WaitOne();
    Console.WriteLine("此时的waitOne会阻挡线程执行,开启set后通过");
    }
    }

    输出结果:

  • manualresetevent中的验证代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    internal class Program
    {
    //static EventWaitHandle my_task1 = new AutoResetEvent(true);//默认自动执行
    //static AutoResetEvent my_task2 = new AutoResetEvent(false);//默认自动阻挡
    static ManualResetEvent my_task3 = new ManualResetEvent(false);//默认手动阻挡
    private static void Main(string[] args)
    {
    //thread
    new Thread(func1).Start();

    new Thread(func2).Start();

    new Thread(func3).Start();

    new Thread(func4).Start();

    my_task3.Set();//手动set一次,执行多个线程

    Timer my_time = new Timer(CloseDown,null,0,2000);//2秒计时器

    Console.ReadKey();
    }
    static void func1()
    {
    my_task3.WaitOne();
    Console.WriteLine("手动第一次执行");
    }
    static void func2()
    {
    my_task3.WaitOne();
    Console.WriteLine("手动第二次执行");
    }
    static void func3()
    {
    my_task3.WaitOne();
    Console.WriteLine("手动第三次执行");
    }
    static void func4()
    {
    Thread.Sleep(3000);//等待3秒,大于reset时间,故不会执行
    my_task3.WaitOne();
    Console.WriteLine("不会执行第四次");
    }

    /// <summary>
    /// 2秒后关闭线程
    /// </summary>
    /// <param name="o"></param>
    static void CloseDown(object o)
    {
    my_task3.Reset();
    }

    }

    输出结果:

6. 总结

  • auto一次只能执行一个线程
  • manual开启后可以同步执行超多线程
  • AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();
  • 多个线程的暂停,继续,可以选择Manual。

7. Update

  • ResetEvent类是个机制,用来控制线程的执行顺序,但是并不意味线程执行只能通过这一种机制实现。
  • 如果程序中一部分Task纳入了ResetEvent中,一部分没有,那么没有被纳入的部分并不会受到该机制的任何影响。
  • WaitOne()方法写在想要被管理的task中;相当于把该Task按顺序标记并阻挡在等待放行的队列中,然后通过Reset()和Set()方法进行关闭和放行。

C sharp中使用AutoResetEvent或者ManualResetEvent做线程管理
http://example.com/2024/07/21/C sharp中使用AutoResetEvent或者ManualResetEvent做线程管理/
作者
xiao cuncun
发布于
2024年7月21日
许可协议