.NET에서 ManualResetEvent와 AutoResetEvent의 차이점은 무엇입니까?


답변:


920

예. 그것은 tollbooth와 문의 차이점과 같습니다. 는 ManualResetEvent수동 (리셋)을 폐쇄 할 필요가 문이다. 이것은 AutoResetEvent유료 자동차이며, 한 자동차가 지나가고 다음 자동차가 통과하기 전에 자동으로 닫힙니다.


166
그것은 훌륭한 비유입니다.
twk

더 나쁜 것은 ARE를 WaitOne으로 설정하는 데 오래 걸리지 않으면 그 동안 재설정됩니다. 그것에 많은 버려진 스레드가있었습니다.
Oliver Friedrich

24
또는 문과 개찰구처럼.
Constantin

9
그렇기 때문에 그들이 이름이 붙여진 이유입니다.
Arlen Beiler

1
@DanGoldstein, 이것이 닫히지 않았고 다른 누군가가 그것을 원할 경우를 대비해서 : msdn.microsoft.com/en-us/library/…
Phil N DeBlanc

124

그냥 상상이 AutoResetEvent가 실행 WaitOne()Reset()단일 원자 작업으로.


16
ManualResetEvent 이벤트에서 WaitOne 및 Reset을 단일 원자 작업으로 실행 한 경우를 제외하고는 여전히 AutoResetEvent와 다른 작업을 수행합니다. ManualResetEvent는 모든 대기 스레드를 동시에 해제합니다. AutoResetEvent는 대기 스레드 하나만 해제하도록 보장합니다.
Martin Brown

55

짧은 대답은 그렇습니다. 가장 중요한 차이점은 AutoResetEvent는 하나의 단일 대기 스레드 만 계속할 수 있다는 것입니다. 반면에 ManualResetEvent는 스레드가 중지하도록 지시 할 때까지 (다시 설정) 계속할 수 있도록합니다.


36

Joseph Albahari가 작성한 C # 3.0 간단히보기

C #에서의 스레딩-무료 전자 책

ManualResetEvent는 AutoResetEvent의 변형입니다. 스레드가 WaitOne 호출에서 통과 된 후에 자동으로 재설정되지 않는다는 점이 다릅니다. 따라서 게이트와 같은 기능을합니다. 호출 Set은 게이트를 열어 게이트에서 WaitOne이 통과하는 스레드 수를 허용합니다. Reset을 호출하면 게이트가 닫히고 다음에 열 때까지 웨이터 큐가 누적 될 수 있습니다.

"스핀 슬리핑 (spin-sleeping)"과 함께 부울 "gateOpen"필드 (휘발성 키워드로 선언)를 사용하여이 기능을 시뮬레이션 할 수 있습니다. 플래그를 반복해서 확인한 다음 짧은 시간 동안 슬리핑합니다.

ManualResetEvents는 특정 작업이 완료되었거나 스레드의 초기화가 완료되어 작업을 수행 할 준비가되었음을 나타내는 데 사용됩니다.


19

ManualResetEventvs의 이해를 명확히하기 위해 간단한 예제를 만들었습니다 AutoResetEvent.

AutoResetEvent: 3 개의 작업자 스레드가 있다고 가정합니다. 해당 스레드 중 하나라도 호출 WaitOne()하면 다른 두 스레드가 모두 실행을 중지하고 신호를 기다립니다. 나는 그들이 사용하고 있다고 가정 WaitOne()합니다. 그것은 같다; 내가 일하지 않으면 아무도 일하지 않습니다. 첫 번째 예에서

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

호출하면 Set()모든 스레드가 작동하고 신호를 기다립니다. 1 초 후에 두 번째 신호를 보내고 실행하고 기다립니다 ( WaitOne()). 이 사람들은 축구 팀 선수라고 생각하십시오. 한 선수가 내가 관리자에게 전화를 할 때까지 기다릴 것이라고 말하면 다른 사람들은 관리자가 계속할 것을 기다릴 때까지 기다릴 것입니다 ( 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();
        }
    }
}

이 예제에서는 처음 적중했을 때 Set()모든 스레드가 종료되고 1 초 후에 모든 스레드가 대기하도록 신호를 보냅니다. WaitOne()내부 전화를 불문하고 다시 설정하면 수동으로 전화 Reset()를 걸어 모두 중지해야 하므로 계속 실행 됩니다.

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

부상당한 선수에 관계없이 심판 / 선수의 관계에 대한 자세한 내용이며 다른 선수의 경기를 계속 기다릴 것입니다. 심판이 대기 ( 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();
        }
    }
}

13

autoResetEvent.WaitOne()

비슷하다

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

원자 작업으로


이것은 개념적으로 만 정확하지만 실제로는 아닙니다. WaitOne과 Reset 사이에서 컨텍스트 전환이 발생할 수 있습니다. 이것은 미묘한 버그로 이어질 수 있습니다.
hofingerandi

2
지금 투표 해 주시겠습니까? 실제로 두 번째 코드 블록을 실제로 수행하는 사람은 없습니다. 차이점을 이해해야합니다.
vezenkov

11

일반적으로 동일한 스레드에 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);
        }
    }

}

자동 리셋 이벤트 출력


이것은 모든 것을 이해하고, 코드를 복사하고, 몇 가지 사항을 변경하는 동안 모두 실행하고, 잘 이해하는 가장 좋은 방법이었습니다.
JohnChris

8

AutoResetEvent 는 메모리에 부울 변수를 유지합니다. 부울 변수가 false이면 스레드를 차단하고 부울 변수가 true이면 스레드를 차단 해제합니다.

AutoResetEvent 객체를 인스턴스화하면 생성자에서 기본값 인 부울 값을 전달합니다. 다음은 AutoResetEvent 객체를 인스턴스화하는 구문입니다.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

WaitOne 방법

이 메소드는 현재 스레드를 차단하고 다른 스레드의 신호를 기다립니다. WaitOne 메소드는 현재 스레드를 휴면 스레드 상태로 만듭니다. 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를 리턴하고 루프를 종료하고 "Thread got signal"을 인쇄합니다.

방법 설정

AutoResetEvent Set 메소드는 작업을 진행하기 위해 대기 스레드에 신호를 보냈습니다. 다음은 Set 메소드를 호출하는 구문입니다.

autoResetEvent.Set();

ManualResetEvent 는 메모리에 부울 변수를 유지합니다. 부울 변수가 false이면 모든 스레드를 차단하고 부울 변수가 true이면 모든 스레드를 차단 해제합니다.

ManualResetEvent를 인스턴스화 할 때 기본 부울 값으로 초기화합니다.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

위의 코드에서 ManualResetEvent를 false 값으로 초기화합니다. 즉, 일부 스레드가 Set () 메서드를 호출 할 때까지 WaitOne 메서드를 호출하는 모든 스레드가 차단됩니다.

ManualResetEvent를 true 값으로 초기화하면 WaitOne 메소드를 호출하는 모든 스레드가 차단되지 않고 계속 진행할 수 있습니다.

WaitOne 방법

이 메소드는 현재 스레드를 차단하고 다른 스레드의 신호를 기다립니다. 신호를 받으면 true를, 그렇지 않으면 false를 반환합니다.

다음은 WaitOne 메소드를 호출하는 구문입니다.

manualResetEvent.WaitOne();

WaitOne 메소드의 두 번째 과부하에서는 현재 스레드가 신호를 기다릴 때까지의 시간 간격을 지정할 수 있습니다. 내부 시간 내에 신호를받지 못하면 false를 반환하고 다음 메소드 행으로 이동합니다.

다음은 시간 간격으로 WaitOne 메소드를 호출하는 구문입니다.

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

WaitOne 메서드에 5 초를 지정했습니다. manualResetEvent 객체가 5 초 사이에 신호를받지 못하면 isSignalled 변수를 false로 설정합니다.

방법 설정

이 방법은 모든 대기 스레드에 신호를 보내는 데 사용됩니다. Set () 메서드는 ManualResetEvent 객체 부울 변수를 true로 설정합니다. 대기중인 모든 스레드가 차단 해제되고 계속 진행됩니다.

다음은 Set () 메서드를 호출하는 구문입니다.

manualResetEvent.Set();

리셋 방법

ManualResetEvent 객체에서 Set () 메서드를 호출하면 부울 값은 true로 유지됩니다. 값을 재설정하기 위해 Reset () 메소드를 사용할 수 있습니다. 재설정 방법은 부울 값을 false로 변경합니다.

다음은 Reset 메소드를 호출하는 구문입니다.

manualResetEvent.Reset();

스레드에 신호를 여러 번 보내려면 Set 메서드를 호출 한 후 즉시 Reset 메서드를 호출해야합니다.


7

예. 이것은 절대적으로 맞습니다.

상태를 나타내는 방법으로 ManualResetEvent를 볼 수 있습니다. 무언가가 켜져 있거나 (설정) 꺼져 있습니다 (재설정). 일부 지속 시간이 발생합니다. 해당 상태가 발생하기를 기다리는 모든 스레드가 진행될 수 있습니다.

AutoResetEvent는 신호와 더 비슷합니다. 어떤 일이 발생했음을 한 번에 나타냅니다. 기간이없는 발생. 일반적으로 발생하는 "무언가"는 아니지만 작은 것으로 단일 스레드에서 처리해야하므로 단일 스레드가 이벤트를 사용한 후 자동 재설정됩니다.


7

네 맞습니다.

이 두 가지를 사용하여 아이디어를 얻을 수 있습니다.

작업이 완료되었음을 알리고 다른 스레드 (스레드)가이 작업을 계속 진행할 수있게하려면 ManualResetEvent를 사용해야합니다.

모든 리소스에 대한 상호 배타적 액세스 권한이 필요한 경우 AutoResetEvent를 사용해야합니다.


1

AutoResetEvent 및 ManualResetEvent를 이해하려면 스레딩이 아니라 인터럽트를 이해해야합니다!

.NET은 저수준 프로그래밍을 가능한 한 멀리 활용하기를 원합니다.

인터럽트는 로우 레벨 프로그래밍에서 사용되는 것으로 로우에서 하이로 상승했다는 신호와 같습니다. 이 경우 프로그램은 정상적인 실행을 중단하고 실행 포인터를이 이벤트 를 처리하는 함수로 이동하십시오 .

인터럽트가 발생할 때 가장 먼저해야 할 일은 하드웨어가 이런 식으로 작동하기 때문에 상태 를 재설정 하는 것입니다.

  1. 핀은 신호에 연결되고 하드웨어는 신호 변경을 수신합니다 (신호는 두 가지 상태 만 가질 수 있음).
  2. 신호가 바뀌면 어떤 일이 발생했고 하드웨어가 메모리 변수를 넣은 경우 를 상태에 (신호가 다시 변경 되더라도 이와 같이 유지됨).
  3. 프로그램은 변수 변경 상태를 확인하고 실행을 처리 함수로 옮깁니다.
  4. 여기서이 인터럽트를 다시들을 수있는 가장 먼저해야 할 일은 이 메모리 변수를 발생하지 않은 상태 로 재설정 하는 것입니다.

이것이 ManualResetEvent와 AutoResetEvent의 차이점입니다.
ManualResetEvent가 발생하고 재설정하지 않으면 다음에 발생하면들을 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.