我已经阅读了关于这方面的文档,我想我明白了。AutoResetEvent在代码通过event.WaitOne()时重置,但ManualResetEvent不会。

这对吗?


当前回答

AutoResetEvent在内存中维护一个布尔变量。如果布尔变量为假,则阻塞线程,如果布尔变量为真,则解除阻塞线程。

实例化AutoResetEvent对象时,在构造函数中传递默认值布尔值。下面是实例化AutoResetEvent对象的语法。

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOne方法

该方法阻塞当前线程,等待其他线程发出信号。WaitOne方法将当前线程置于Sleep线程状态。WaitOne方法如果接收到信号则返回true,否则返回false。

autoResetEvent.WaitOne();

WaitOne方法的秒重载等待指定的秒数。如果它没有得到任何信号,线程继续它的工作。

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

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

我们通过传递2秒作为参数来调用WaitOne方法。在while循环中,它等待信号2秒,然后继续它的工作。当线程得到信号时,WaitOne返回true并退出循环并打印“线程得到信号”。

设置方法

方法将信号发送给等待线程以继续其工作。下面是调用Set方法的语法。

autoResetEvent.Set();

ManualResetEvent在内存中维护一个布尔变量。当布尔变量为false时,它阻塞所有线程,当布尔变量为true时,它解除阻塞所有线程。

当我们实例化ManualResetEvent时,我们用默认的布尔值初始化它。

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

在上面的代码中,我们用false值初始化ManualResetEvent,这意味着所有调用WaitOne方法的线程都将阻塞,直到一些线程调用Set()方法。

如果我们初始化ManualResetEvent的值为true,那么所有调用WaitOne方法的线程都不会阻塞,可以继续执行。

WaitOne方法

该方法阻塞当前线程,等待其他线程发出信号。如果它接收到信号,则返回true否则返回false。

下面是调用WaitOne方法的语法。

manualResetEvent.WaitOne();

在WaitOne方法的第二次重载中,我们可以指定当前线程等待信号的时间间隔。如果在time internal内,它没有接收到信号,则返回false并进入下一行方法。

下面是按时间间隔调用WaitOne方法的语法。

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

我们已经在WaitOne方法中指定了5秒。如果manualResetEvent对象在5秒内没有接收到信号,它将issignals变量设置为false。

设置方法

此方法用于向所有等待线程发送信号。Set()方法将ManualResetEvent对象布尔变量设置为true。所有等待的线程都被解除阻塞并继续执行。

下面是调用Set()方法的语法。

manualResetEvent.Set();

复位方法

一旦我们在ManualResetEvent对象上调用Set()方法,它的布尔值仍然为true。要重置值,可以使用reset()方法。重置方法将布尔值更改为false。

下面是调用Reset方法的语法。

manualResetEvent.Reset();

如果要多次向线程发送信号,必须在调用Set方法后立即调用Reset方法。

其他回答

是的。这就像收费站和门的区别。ManualResetEvent是门,需要手动关闭(重置)。AutoResetEvent是一个收费站,允许一辆车通过,在下一辆车通过之前自动关闭。

想象一下AutoResetEvent将WaitOne()和Reset()作为单个原子操作执行。

AutoResetEvent还保证只释放一个等待线程。

好吧,通常在同一个线程中添加2个答案不是一个好的做法,但我不想编辑/删除我之前的答案,因为它可以在另一种方式上有所帮助。

现在,我创建了一个更全面、更容易理解的控制台应用程序片段。

只需在两个不同的控制台上运行示例,并观察行为。你会对幕后发生的事情有更清楚的了解。

手动复位事件

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);
        }
    }

}

自动重置事件

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);
        }
    }

}

摘自c# 3.0果壳书,由 约瑟夫Albahari

c#线程-免费电子书

ManualResetEvent是AutoResetEvent的变体。它的不同之处在于它不会在线程通过WaitOne调用后自动重置,因此功能类似于gate:调用Set打开gate,允许WaitOne在gate处通过任意数量的线程;调用Reset会关闭大门,可能会导致排队等候的人越来越多,直到下一个门被打开。

可以使用布尔值“gateOpen”字段(使用volatile关键字声明)结合“spin-sleep”来模拟此功能——重复检查标志,然后小睡一小段时间。

ManualResetEvents有时用于表示特定操作已完成,或线程已完成初始化并准备执行工作。

autoResetEvent.WaitOne ()

类似于

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

作为原子操作