简体   繁体   English

.NET中的ManualResetEvent和AutoResetEvent有什么区别?

[英]What is the difference between ManualResetEvent and AutoResetEvent in .NET?

I have read the documentation on this and I think I understand. 我已阅读有关此文档,我想我明白了。 An AutoResetEvent resets when the code passes through event.WaitOne() , but a ManualResetEvent does not. 当代码通过event.WaitOne()AutoResetEvent重置,但ManualResetEvent则不会。

Is this correct? 它是否正确?

Yes. 是。 It's like the difference between a tollbooth and a door. 这就像收费站和门之间的区别。 The ManualResetEvent is the door, which needs to be closed (reset) manually. ManualResetEvent是门,需要手动关闭(重置)。 The AutoResetEvent is a tollbooth, allowing one car to go by and automatically closing before the next one can get through. AutoResetEvent是一个收费站,允许一辆汽车经过并在下一辆汽车通过之前自动关闭。

试想一下, AutoResetEvent WaitOne()Reset()作为单个原子操作执行。

The short answer is yes. 简短的回答是肯定的。 The most important difference is that an AutoResetEvent will only allow one single waiting thread to continue. 最重要的区别是AutoResetEvent只允许一个等待的线程继续。 A ManualResetEvent on the other hand will keep allowing threads, several at the same time even, to continue until you tell it to stop (Reset it). 另一方面,ManualResetEvent将继续允许线程(几个同时均匀)继续,直到你告诉它停止(重置它)。

Taken from C# 3.0 Nutshell book, by Joseph Albahari 取自Joseph#Albahari的C#3.0 Nutshell书

Threading in C# - Free E-Book C#中的线程 - 免费电子书

A ManualResetEvent is a variation on AutoResetEvent. ManualResetEvent是AutoResetEvent的变体。 It differs in that it doesn't automatically reset after a thread is let through on a WaitOne call, and so functions like a gate: calling Set opens the gate, allowing any number of threads that WaitOne at the gate through; 它的不同之处在于它在WaitOne调用中通过线程后不会自动复位,因此像gate:call Set这样的函数会打开门,允许任何数量的线程在门口通过WaitOne; calling Reset closes the gate, causing, potentially, a queue of waiters to accumulate until its next opened. 调用Reset关闭门,可能导致服务器队列累积,直到下一次打开。

One could simulate this functionality with a boolean "gateOpen" field (declared with the volatile keyword) in combination with "spin-sleeping" – repeatedly checking the flag, and then sleeping for a short period of time. 可以使用布尔“gateOpen”字段(使用volatile关键字声明)与“spin-sleeping”结合来模拟此功能 ​​- 重复检查标志,然后在短时间内休眠。

ManualResetEvents are sometimes used to signal that a particular operation is complete, or that a thread's completed initialization and is ready to perform work. ManualResetEvents有时用于表示特定操作已完成,或者线程已完成初始化并准备好执行工作。

I created simple examples to clarify understanding of ManualResetEvent vs AutoResetEvent . 我创建了简单的示例来阐明对ManualResetEventAutoResetEvent理解。

AutoResetEvent : lets assume you have 3 workers thread. AutoResetEvent :假设您有3个工作线程。 If any of those threads will call WaitOne() all other 2 threads will stop execution and wait for signal. 如果任何这些线程将调用WaitOne()所有其他2个线程将停止执行并等待信号。 I am assuming they are using WaitOne() . 我假设他们正在使用WaitOne() It is like; 它像是; if I do not work, nobody works. 如果我不工作,没人会工作。 In first example you can see that 在第一个例子中,你可以看到

autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();

When you call Set() all threads will work and wait for signal. 当你调用Set()所有线程都会工作并等待信号。 After 1 second I am sending second signal and they execute and wait ( WaitOne() ). 1秒钟后,我发送第二个信号,然后执行并等待( WaitOne() )。 Think about these guys are soccer team players and if one player says I will wait until manager calls me, and others will wait until manager tells them to continue ( Set() ) 想想这些家伙是足球队员,如果有一名球员说我会等到经理给我打电话,其他人会等到经理告诉他们继续( Set()

public class AutoResetEventSample
{
    private AutoResetEvent autoReset = new AutoResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        autoReset.Set();
        Thread.Sleep(1000);
        autoReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
}

In this example you can clearly see that when you first hit Set() it will let all threads go, then after 1 second it signals all threads to wait! 在这个例子中,你可以清楚地看到,当你第一次点击Set()它会让所有线程都进入,然后在1秒后它会发出所有线程等待的信号! As soon as you set them again regardless they are calling WaitOne() inside, they will keep running because you have to manually call Reset() to stop them all. 一旦你再次设置它们而不管它们是否在内部调用WaitOne() ,它们将继续运行,因为你必须手动调用Reset()来停止它们。

manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();

It is more about Referee/Players relationship there regardless of any of the player is injured and wait for playing others will continue to work. 它更多的是关于裁判/球员的关系,无论任何一名球员受伤,等待比赛的人都会继续工作。 If Referee says wait ( Reset() ) then all players will wait until next signal. 如果裁判说等待( Reset() ),则所有玩家将等到下一个信号。

public class ManualResetEventSample
{
    private ManualResetEvent manualReset = new ManualResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        manualReset.Set();
        Thread.Sleep(1000);
        manualReset.Reset();
        Console.WriteLine("Press to release all threads.");
        Console.ReadLine();
        manualReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
}

autoResetEvent.WaitOne()

is similar to 类似于

try
{
   manualResetEvent.WaitOne();
}
finally
{
   manualResetEvent.Reset();
}

as an atomic operation 作为原子操作

OK, normally it does not a good practice to add 2 answers in same thread, but I did not want to edit/delete my previous answer, since it can help on another manner. 好的,通常在同一个帖子中添加2个答案不是一个好习惯,但我不想编辑/删除我以前的答案,因为它可以帮助另一种方式。

Now, I created, much more comprehensive, and easy to understand, run-to-learn console app snippet below. 现在,我在下面创建了更加全面且易于理解的“运行 - 学习”控制台应用程序片段。

Just run the examples on two different consoles, and observe behaviour. 只需在两个不同的控制台上运行示例,并观察行为。 You will get much more clear idea there what is happening behind the scenes. 你会更清楚地了解幕后发生的事情。

Manual Reset Event 手动重置事件

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class ManualResetEventSample
    {
        private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
            Thread.Sleep(10000);
            Console.WriteLine();
            Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

手动重置事件输出

Auto Reset Event 自动重置事件

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class AutoResetEventSample
    {
        private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
            Thread.Sleep(10000);
            Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

自动重置事件输出

Yes, thats right. 恩,那就对了。

You can get an idea by the usage of these two. 您可以通过使用这两个来获得一个想法。

If you need to tell that you are finished with some work and other (threads) waiting for this can now proceed, you should use ManualResetEvent. 如果你需要告诉你已经完成了一些工作而其他(线程)等待这个现在可以继续,你应该使用ManualResetEvent。

If you need to have mutual exclusive access to any resource, you should use AutoResetEvent. 如果您需要对任何资源进行互斥访问,则应使用AutoResetEvent。

Yes. 是。 This is absolutely correct. 这绝对是正确的。

You could see ManualResetEvent as a way to indicate state. 您可以将ManualResetEvent视为指示状态的方式。 Something is on (Set) or off (Reset). 某些东西打开(设置)或关闭(重置)。 An occurrence with some duration. 有一段时间的事件。 Any thread waiting for that state to happen can proceed. 等待该状态发生的任何线程都可以继续。

An AutoResetEvent is more comparable to a signal. AutoResetEvent与信号更具可比性。 A one shot indication that something has happened. 一次性表明事情已经发生。 An occurrence without any duration. 没有任何持续时间的事件。 Typically but not necessarily the "something" that has happened is small and needs to be handled by a single thread - hence the automatic reset after a single thread have consumed the event. 通常但不一定发生的“某事”很小并且需要由单个线程处理 - 因此在单个线程消耗了事件之后自动重置。

AutoResetEvent maintains a boolean variable in memory. AutoResetEvent在内存中维护一个布尔变量。 If the boolean variable is false then it blocks the thread and if the boolean variable is true it unblocks the thread. 如果布尔变量为false,则它会阻塞线程,如果布尔变量为true,则取消阻塞线程。

When we instantiate an AutoResetEvent object, we pass the default value of boolean value in the constructor. 当我们实例化一个AutoResetEvent对象时,我们在构造函数中传递boolean value的默认值。 Below is the syntax of instantiate an AutoResetEvent object. 下面是实例化AutoResetEvent对象的语法。

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOne method WaitOne方法

This method blocks the current thread and wait for the signal by other thread. 此方法阻塞当前线程并等待其他线程的信号。 WaitOne method puts the current thread into a Sleep thread state. WaitOne方法将当前线程置于Sleep线程状态。 WaitOne method returns true if it receives the signal else returns false. 如果WaitOne方法收到信号则返回true,否则返回false。

autoResetEvent.WaitOne();

Second overload of WaitOne method wait for the specified number of seconds. WaitOne方法的第二次重载等待指定的秒数。 If it does not get any signal thread continues its work. 如果它没有得到任何信号线程继续其工作。

static void ThreadMethod()
{
    while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
    {
        Console.WriteLine("Continue");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }

    Console.WriteLine("Thread got signal");
}

We called WaitOne method by passing the 2 seconds as arguments. 我们通过传递2秒作为参数来调用WaitOne方法。 In the while loop, it wait for the signal for 2 seconds then it continues its work. 在while循环中,它等待信号2秒然后继续工作。 When the thread got the signal WaitOne returns true and exits the loop and print the "Thread got signal". 当线程得到信号时,WaitOne返回true并退出循环并打印“Thread got signal”。

Set method 设定方法

AutoResetEvent Set method sent the signal to the waiting thread to proceed its work. AutoResetEvent Set方法将信号发送到等待线程以继续其工作。 Below is the syntax of calling Set method. 下面是调用Set方法的语法。

autoResetEvent.Set();

ManualResetEvent maintains a boolean variable in memory. ManualResetEvent在内存中维护一个布尔变量。 When the boolean variable is false then it blocks all threads and when the boolean variable is true it unblocks all threads. 当布尔变量为false时,它会阻塞所有线程,当布尔变量为true时,它会解除阻塞所有线程。

When we instantiate a ManualResetEvent, we initialize it with default boolean value. 当我们实例化ManualResetEvent时,我们使用默认的布尔值初始化它。

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

In the above code, we initialize the ManualResetEvent with false value, that means all the threads which calls the WaitOne method will block until some thread calls the Set() method. 在上面的代码中,我们使用false值初始化ManualResetEvent,这意味着调用WaitOne方法的所有线程都将阻塞,直到某个线程调用Set()方法。

If we initialize ManualResetEvent with true value, all the threads which calls the WaitOne method will not block and free to proceed further. 如果我们使用true值初始化ManualResetEvent,则调用WaitOne方法的所有线程都不会阻塞并自由进一步继续。

WaitOne Method WaitOne方法

This method blocks the current thread and wait for the signal by other thread. 此方法阻塞当前线程并等待其他线程的信号。 It returns true if its receives a signal else returns false. 如果接收到信号则返回true,否则返回false。

Below is the syntax of calling WaitOne method. 下面是调用WaitOne方法的语法。

manualResetEvent.WaitOne();

In the second overload of WaitOne method, we can specify the time interval till the current thread wait for the signal. 在WaitOne方法的第二个重载中,我们可以指定当前线程等待信号的时间间隔。 If within time internal, it does not receives a signal it returns false and goes into the next line of method. 如果在内部时间内,它没有收到信号,它返回false并进入下一行方法。

Below is the syntax of calling WaitOne method with time interval. 下面是使用时间间隔调用WaitOne方法的语法。

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

We have specify 5 seconds into the WaitOne method. 我们在WaitOne方法中指定了5秒。 If the manualResetEvent object does not receives a signal between 5 seconds, it set the isSignalled variable to false. 如果manualResetEvent对象在5秒内没有收到信号,则将isSignalled变量设置为false。

Set Method 设置方法

This method is used for sending the signal to all waiting threads. 此方法用于将信号发送到所有等待的线程。 Set() Method set the ManualResetEvent object boolean variable to true. Set()方法将ManualResetEvent对象的boolean变量设置为true。 All the waiting threads are unblocked and proceed further. 所有等待的线程都被解锁并继续进行。

Below is the syntax of calling Set() method. 下面是调用Set()方法的语法。

manualResetEvent.Set();

Reset Method 重置方法

Once we call the Set() method on the ManualResetEvent object, its boolean remains true. 一旦我们在ManualResetEvent对象上调用Set()方法,它的布尔值保持为true。 To reset the value we can use Reset() method. 要重置值,我们可以使用Reset()方法。 Reset method change the boolean value to false. Reset方法将布尔值更改为false。

Below is the syntax of calling Reset method. 下面是调用Reset方法的语法。

manualResetEvent.Reset();

We must immediately call Reset method after calling Set method if we want to send signal to threads multiple times. 如果我们想多次向线程发送信号,我们必须在调用Set方法后立即调用Reset方法。

If you want to understand AutoResetEvent and ManualResetEvent you need to understand not threading but interrupts! 如果您想了解AutoResetEvent和ManualResetEvent,您需要了解不是线程而是中断!

.NET wants to conjure up low-level programming the most distant possible. .NET希望尽可能地唤起低级编程。

An interrupts is something used in low-level programming which equals to a signal that from low became high (or viceversa). 中断是低级编程中使用的东西,等于从低变高(或反之)的信号。 When this happens the program interrupt its normal execution and move the execution pointer to the function that handles this event . 发生这种情况时,程序会中断其正常执行并将执行指针移动到处理此事件的函数。

The first thing to do when an interrupt happend is to reset its state, becosa the hardware works in this way: 当中断发生时要做的第一件事是重置其状态,因为硬件以这种方式工作:

  1. a pin is connected to a signal and the hardware listen for it to change (the signal could have only two states). 引脚连接到信号,硬件监听它的变化(信号只能有两种状态)。
  2. if the signal changes means that something happened and the hardware put a memory variable to the state happened (and it remain like this even if the signal change again). 如果信号发生变化意味着发生了某些事情并且硬件将一个记忆变量发送到发生的状态(即使信号再次发生变化,它仍然是这样)。
  3. the program notice that variable change states and move the execution to a handling function. 程序注意到变量状态并将执行移动到处理函数。
  4. here the first thing to do, to be able to listen again this interrupt, is to reset this memory variable to the state not-happened. 这里要做的第一件事,就是能够再次监听这个中断,就是这个内存变量重置为未发生的状态。

This is the difference between ManualResetEvent and AutoResetEvent. 这是ManualResetEvent和AutoResetEvent之间的区别。
If a ManualResetEvent happen and I do not reset it, the next time it happens I will not be able to listen it. 如果发生了ManualResetEvent并且我没有重置它,下次它发生时我将无法收听它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 AutoResetEvent和ManualResetEvent之间的混合 - Hybrid between AutoResetEvent and ManualResetEvent AutoResetEvent / ManualResetEvent使用哪些资源? - What resources do AutoResetEvent / ManualResetEvent consume? AutoResetEvent 和 Mutex 有什么区别 - Whats is the difference between AutoResetEvent and Mutex 为什么在C#中使用AutoResetEvent和ManualResetEvent时,我的代码输出为何不同? - Why the outputs of my code is difference when using AutoResetEvent and ManualResetEvent in C# AutoResetEvent和信号量与maximumCount = 1之间有什么区别吗? - Is there any difference between an AutoResetEvent and a Semaphore with maximumCount = 1? Thread.Sleep(timeout)和ManualResetEvent.Wait(timeout)有什么区别? - What is the difference between Thread.Sleep(timeout) and ManualResetEvent.Wait(timeout)? 如何在C#中检查AutoResetEvent或ManualResetEvent的阻塞状态? - How to check the blocking state of AutoResetEvent or ManualResetEvent in C#? 是否有.Net类来执行ManualResetEvent.PulseAll()(如果存在)会做什么? - Is there a .Net class to do what ManualResetEvent.PulseAll() would do (if it existed)? .net 4.0和.net 4.0.3有什么区别? - What is the difference between .net 4.0 and .net 4.0.3? .NET中的错误和异常有什么区别? - What is the difference between an error and an exception in .NET?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM