zoukankan      html  css  js  c++  java
  • AutoResetEvent和ManualResetEvent的异同

    一般情况下多线程总是无序的,要使其按照一定的顺序执行(比如B线程必须在A线程执行完毕之后才执行)。可以采用调用Thread的Join方式,也可以使用“信号”类的方式完成此任务。常见的信号类有AutoResetEvent和ManualResetEvent。一般情况下,可以给它们的默认构造函数传入false,这就表示当前初始化该信号源准备发送信号,当调用了WaitOne方法的时候把当前的线程阻塞,直到接受到信号为止(发送信号使用Set函数)。下面是一个例子:

    [C#]

    public class Example
        {
            /// <summary>
            /// 预备信号,准备发送,初始化
            /// </summary>
            public static AutoResetEvent flag = new AutoResetEvent(false);
    
            public void Begin()
            {
                Thread th = new Thread(() => 
                {
                    for (int i = 1; i < 6; i++)
                    {
                        Thread.Sleep(1000);
                        Console.WriteLine("子线程数据:"+i);
                    }
                    flag.Set();
                });
    
                th.IsBackground = true;
                th.Start();
            }
    
            static void Main(string[] args)
            {
                Example e = new Example();
                e.Begin();
                //等待该信号接受到完毕,才执行主线程中的任务循环
                flag.WaitOne();
                for (int i = 1; i < 6; i++)
                {
                      Thread.Sleep(1000);
                      Console.WriteLine("主线程数据:"+i);
                }
            }
        }

    [VB.NET]

    Public Class Example
        ''' <summary>
        ''' 预备信号,准备发送,初始化
        ''' </summary>
        Public Shared flag As New AutoResetEvent(False)
    
        Public Sub Begin()
            Dim th As New Thread(Function() Do
                For i As Integer = 1 To 5
                    Thread.Sleep(1000)
                    Console.WriteLine("子线程数据:" + i)
                Next
                flag.[Set]()
            End Function)
    
            th.IsBackground = True
            th.Start()
        End Sub
    
        Private Shared Sub Main(args As String())
            Dim e As New Example()
            e.Begin()
            '等待该信号接受到完毕,才执行主线程中的任务循环
            flag.WaitOne()
            For i As Integer = 1 To 5
                Thread.Sleep(1000)
                Console.WriteLine("主线程数据:" + i)
            Next
        End Sub
    
    End Class

    上面的例子换成ManualResetEvent亦如此。

    那么它们区别在何处呢?其实单从一个线程根本看不出区别。下面我们看两个延时不同的线程同时访问一个AutoResetEvent、ManualResetEvent的例子:

    [C#]

    public class Example
        {
            /// <summary>
            /// 预备信号,准备发送,初始化
            /// </summary>
            public AutoResetEvent flag = new AutoResetEvent(false);
    
            public void Begin()
            {
                Thread th1 = new Thread(() => 
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("第一个线程已经通过……");
                    flag.Set();
                });
    
                Thread th2 = new Thread(() =>
                {
                    Thread.Sleep(500);
                    Console.WriteLine("第二个线程已经通过……");
                    flag.Set();
                });
    
                th1.IsBackground = true;
                th1.Start();
                th2.IsBackground = true;
                th2.Start();
                flag.WaitOne();
                flag.WaitOne();
            }
    
            static void Main(string[] args)
            {
                Example e = new Example();
                e.Begin();
            }
    
        }

    [VB.NET]

    Public Class Example
        ''' <summary>
        ''' 预备信号,准备发送,初始化
        ''' </summary>
        Public flag As New AutoResetEvent(False)
    
        Public Sub Begin()
            Dim th1 As New Thread(Function() Do
                Thread.Sleep(1000)
                Console.WriteLine("第一个线程已经通过……")
                flag.[Set]()
            End Function)
    
            Dim th2 As New Thread(Function() Do
                Thread.Sleep(500)
                Console.WriteLine("第二个线程已经通过……")
                flag.[Set]()
            End Function)
    
            th1.IsBackground = True
            th1.Start()
            th2.IsBackground = True
            th2.Start()
            flag.WaitOne()
            flag.WaitOne()
        End Sub
    
        Private Shared Sub Main(args As String())
            Dim e As New Example()
            e.Begin()
        End Sub
    End Class

    因为第一个线程延时1秒,第二个线程延时0.5秒,此时Begin函数中遇到了WaitOne,主线程等待子线程完成任务(因为只有任意一个线程向主线程发送“完成”的信号,即调用Set函数之后主线程方可继续)。因此第一个WaitOne的时候由于第二个线程延时小于第一个线程,因此第二个线程先完成并输出内容;主线程接着又碰到了WaitOne,此时由于第一次的Set已经把AutoResetEvent重新设置为false,所以WaitOne又开始继续等待——直到第一个线程完成为止。
    因此我们可以得出一个结论:AutoResetEvent在收到信号之后立即可以再次WaitOne,从而等待第二次、第三次……乃至更多次的Set,每次Set之后AutoResetEvent都会自动初始化为false,为下一次WaitOne做准备

    但是ManualResetEvent则不然。如果你把上面的替换成该类的话只输出“第二个线程通过”——究其原因,是因为在第一个WaitOne被Set之后,其内部构造函数的那个“false”已经被替换成了“true”,因此再也不会进行下一轮的Wait。

    解决方法是在两个WaitOne之间调用Reset方法——这样使得你的ManualResetEvent在第一次被Set之后初始化成false的状态(预发信号状态),准备第二次的WaitOne。

  • 相关阅读:
    JAVA——俄罗斯方块
    JAVA——简单科学计算器设计
    标准9*9数独破解器
    k短路算法(A*)
    洛谷2939 分层图模板
    PCA算法
    coursera-斯坦福-机器学习-吴恩达-笔记week4
    coursera-斯坦福-机器学习-吴恩达-笔记week3
    coursera-斯坦福-机器学习-吴恩达-笔记week2
    coursera-斯坦福-机器学习-吴恩达-笔记week1
  • 原文地址:https://www.cnblogs.com/ServiceboyNew/p/2633622.html
Copyright © 2011-2022 走看看