달력

5

« 2024/5 »

  • 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
2017. 3. 5. 04:52

Thread 관련 프로그래밍/C#2017. 3. 5. 04:52


  1. ManualResetEvent
  2. AutoResetEvent
  3. AutoResetEvent 클래스 (http://www.csharpstudy.com)
  4. ManualResetEvent 클래스 (http://www.csharpstudy.com)
  5. CountdownEvent 클래스 (http://www.csharpstudy.com)




AutoResetEvent

1. Set() 을 호출하면 Wait 하고 있는 쓰레드를 중에서 처음 한개만 통과시키고 나머지는 대기시킴

2. 다시 Set() 호출하면 그 다음 쓰레드를 통과시키고 나머지는 대기시킴.


ManualResetEvent

1. Set() 을 호출하면 Wait 하고 있는 쓰레드 모두 통과시킴. (계속 통과 상태)

2. Reset() 을 호출하면 다시 대기 상태



ManualResetEvent Class


https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx


Notifies one or more waiting threads that an event has occurred. This class cannot be inherited.

Namespace:   System.Threading
Assembly:  mscorlib (in mscorlib.dll)

Inheritance Hierarchy

System.Object
  System.MarshalByRefObject
    System.Threading.WaitHandle
      System.Threading.EventWaitHandle
        System.Threading.ManualResetEvent




Remarks

In the .NET Framework version 2.0, ManualResetEvent derives from the new EventWaitHandle class. A ManualResetEvent is functionally equivalent to an EventWaitHandle created with EventResetMode.ManualReset.

System_CAPS_noteNote

Unlike the ManualResetEvent class, the EventWaitHandle class provides access to named system synchronization events.

ManualResetEvent allows threads to communicate with each other by signaling. Typically, this communication concerns a task which one thread must complete before other threads can proceed.

When a thread begins an activity that must complete before other threads proceed, it calls Reset to put ManualResetEvent in the non-signaled state. This thread can be thought of as controlling the ManualResetEvent. Threads that call WaitOne on the ManualResetEvent will block, awaiting the signal. When the controlling thread completes the activity, it calls Set to signal that the waiting threads can proceed. All waiting threads are released.

Once it has been signaled, ManualResetEvent remains signaled until it is manually reset. That is, calls to WaitOne return immediately.

You can control the initial state of a ManualResetEvent by passing a Boolean value to the constructor, true if the initial state is signaled and false otherwise.

ManualResetEvent can also be used with the staticWaitAll and WaitAny methods.

For more information about thread synchronization mechanisms, see ManualResetEvent and ManualResetEventSlim in the conceptual documentation.

Examples

The following example demonstrates how ManualResetEvent works. The example starts with a ManualResetEvent in the unsignaled state (that is, false is passed to the constructor). The example creates three threads, each of which blocks on the ManualResetEvent by calling its WaitOne method. When the user presses the Enter key, the example calls the Set method, which releases all three threads. Contrast this with the behavior of the AutoResetEvent class, which releases threads one at a time, resetting automatically after each release.

Pressing the Enter key again demonstrates that the ManualResetEvent remains in the signaled state until its Reset method is called: The example starts two more threads. These threads do not block when they call the WaitOne method, but instead run to completion.

Pressing the Enter key again causes the example to call the Reset method and to start one more thread, which blocks when it calls WaitOne. Pressing the Enter key one final time calls Set to release the last thread, and the program ends.

using System;
using System.Threading;

public class Example
{
    // mre is used to block and release threads manually. It is
    // created in the unsignaled state.
    private static ManualResetEvent mre = new ManualResetEvent(false);

    static void Main()
    {
        Console.WriteLine("\nStart 3 named threads that block on a ManualResetEvent:\n");

        for(int i = 0; i <= 2; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }

        Thread.Sleep(500);
        Console.WriteLine("\nWhen all three threads have started, press Enter to call Set()" +
                          "\nto release all the threads.\n");
        Console.ReadLine();

        mre.Set();

        Thread.Sleep(500);
        Console.WriteLine("\nWhen a ManualResetEvent is signaled, threads that call WaitOne()" +
                          "\ndo not block. Press Enter to show this.\n");
        Console.ReadLine();

        for(int i = 3; i <= 4; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }

        Thread.Sleep(500);
        Console.WriteLine("\nPress Enter to call Reset(), so that threads once again block" +
                          "\nwhen they call WaitOne().\n");
        Console.ReadLine();

        mre.Reset();

        // Start a thread that waits on the ManualResetEvent.
        Thread t5 = new Thread(ThreadProc);
        t5.Name = "Thread_5";
        t5.Start();

        Thread.Sleep(500);
        Console.WriteLine("\nPress Enter to call Set() and conclude the demo.");
        Console.ReadLine();

        mre.Set();

        // If you run this example in Visual Studio, uncomment the following line:
        //Console.ReadLine();
    }


    private static void ThreadProc()
    {
        string name = Thread.CurrentThread.Name;

        Console.WriteLine(name + " starts and calls mre.WaitOne()");

        mre.WaitOne();

        Console.WriteLine(name + " ends.");
    }
}

/* This example produces output similar to the following:

Start 3 named threads that block on a ManualResetEvent:

Thread_0 starts and calls mre.WaitOne()
Thread_1 starts and calls mre.WaitOne()
Thread_2 starts and calls mre.WaitOne()

When all three threads have started, press Enter to call Set()
to release all the threads.


Thread_2 ends.
Thread_0 ends.
Thread_1 ends.

When a ManualResetEvent is signaled, threads that call WaitOne()
do not block. Press Enter to show this.


Thread_3 starts and calls mre.WaitOne()
Thread_3 ends.
Thread_4 starts and calls mre.WaitOne()
Thread_4 ends.

Press Enter to call Reset(), so that threads once again block
when they call WaitOne().


Thread_5 starts and calls mre.WaitOne()

Press Enter to call Set() and conclude the demo.

Thread_5 ends.
 */





AutoResetEvent


https://msdn.microsoft.com/en-us/library/system.threading.autoresetevent(v=vs.110).aspx


Notifies a waiting thread that an event has occurred. This class cannot be inherited.

Namespace:   System.Threading
Assembly:  mscorlib (in mscorlib.dll)

Inheritance Hierarchy

System.Object
  System.MarshalByRefObject
    System.Threading.WaitHandle
      System.Threading.EventWaitHandle
        System.Threading.AutoResetEvent




Remarks

AutoResetEvent allows threads to communicate with each other by signaling. Typically, you use this class when threads need exclusive access to a resource.

System_CAPS_importantImportant

This type implements the IDisposable interface. When you have finished using the type, you should dispose of it either directly or indirectly. To dispose of the type directly, call its Dispose method in a try/catch block. To dispose of it indirectly, use a language construct such as using (in C#) or Using (in Visual Basic). For more information, see the “Using an Object that Implements IDisposable” section in the IDisposable interface topic.

A thread waits for a signal by calling WaitOne on the AutoResetEvent. If the AutoResetEvent is in the non-signaled state, the thread blocks, waiting for the thread that currently controls the resource to signal that the resource is available by calling Set.

Calling Set signals AutoResetEvent to release a waiting thread. AutoResetEvent remains signaled until a single waiting thread is released, and then automatically returns to the non-signaled state. If no threads are waiting, the state remains signaled indefinitely.

If a thread calls WaitOne while the AutoResetEvent is in the signaled state, the thread does not block. The AutoResetEvent releases the thread immediately and returns to the non-signaled state.

System_CAPS_importantImportant

There is no guarantee that every call to the Set method will release a thread. If two calls are too close together, so that the second call occurs before a thread has been released, only one thread is released. It is as if the second call did not happen. Also, if Set is called when there are no threads waiting and the AutoResetEvent is already signaled, the call has no effect.

You can control the initial state of an AutoResetEvent by passing a Boolean value to the constructor: true if the initial state is signaled and false otherwise.

AutoResetEvent can also be used with the staticWaitAll and WaitAny methods.

For more information about thread synchronization mechanisms, see AutoResetEvent in the conceptual documentation.

Beginning with the .NET Framework version 2.0, AutoResetEvent derives from the new EventWaitHandle class. An AutoResetEvent is functionally equivalent to an EventWaitHandle created with EventResetMode.AutoReset.

System_CAPS_noteNote

Unlike the AutoResetEvent class, the EventWaitHandle class provides access to named system synchronization events.

Examples

The following example shows how to use AutoResetEvent to release one thread at a time, by calling the Set method (on the base class) each time the user presses the Enter key. The example starts three threads, which wait on an AutoResetEvent that was created in the signaled state. The first thread is released immediately, because the AutoResetEvent is already in the signaled state. This resets the AutoResetEvent to the non-signaled state, so that subsequent threads block. The blocked threads are not released until the user releases them one at a time by pressing the Enter key.

After the threads are released from the first AutoResetEvent, they wait on another AutoResetEvent that was created in the non-signaled state. All three threads block, so the Set method must be called three times to release them all.

using System;
using System.Threading;

// Visual Studio: Replace the default class in a Console project with 
//                the following class.
class Example
{
    private static AutoResetEvent event_1 = new AutoResetEvent(true);
    private static AutoResetEvent event_2 = new AutoResetEvent(false);

    static void Main()
    {
        Console.WriteLine("Press Enter to create three threads and start them.\r\n" +
                          "The threads wait on AutoResetEvent #1, which was created\r\n" +
                          "in the signaled state, so the first thread is released.\r\n" +
                          "This puts AutoResetEvent #1 into the unsignaled state.");
        Console.ReadLine();

        for (int i = 1; i < 4; i++)
        {
            Thread t = new Thread(ThreadProc);
            t.Name = "Thread_" + i;
            t.Start();
        }
        Thread.Sleep(250);

        for (int i = 0; i < 2; i++)
        {
            Console.WriteLine("Press Enter to release another thread.");
            Console.ReadLine();
            event_1.Set();
            Thread.Sleep(250);
        }

        Console.WriteLine("\r\nAll threads are now waiting on AutoResetEvent #2.");
        for (int i = 0; i < 3; i++)
        {
            Console.WriteLine("Press Enter to release a thread.");
            Console.ReadLine();
            event_2.Set();
            Thread.Sleep(250);
        }

        // Visual Studio: Uncomment the following line.
        //Console.Readline();
    }

    static void ThreadProc()
    {
        string name = Thread.CurrentThread.Name;

        Console.WriteLine("{0} waits on AutoResetEvent #1.", name);
        event_1.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #1.", name);

        Console.WriteLine("{0} waits on AutoResetEvent #2.", name);
        event_2.WaitOne();
        Console.WriteLine("{0} is released from AutoResetEvent #2.", name);

        Console.WriteLine("{0} ends.", name);
    }
}

/* This example produces output similar to the following:

Press Enter to create three threads and start them.
The threads wait on AutoResetEvent #1, which was created
in the signaled state, so the first thread is released.
This puts AutoResetEvent #1 into the unsignaled state.

Thread_1 waits on AutoResetEvent #1.
Thread_1 is released from AutoResetEvent #1.
Thread_1 waits on AutoResetEvent #2.
Thread_3 waits on AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #1.
Press Enter to release another thread.

Thread_3 is released from AutoResetEvent #1.
Thread_3 waits on AutoResetEvent #2.
Press Enter to release another thread.

Thread_2 is released from AutoResetEvent #1.
Thread_2 waits on AutoResetEvent #2.

All threads are now waiting on AutoResetEvent #2.
Press Enter to release a thread.

Thread_2 is released from AutoResetEvent #2.
Thread_2 ends.
Press Enter to release a thread.

Thread_1 is released from AutoResetEvent #2.
Thread_1 ends.
Press Enter to release a thread.

Thread_3 is released from AutoResetEvent #2.
Thread_3 ends.
 */


1. signaled 상태에서는 Set() 호출해도 non-signaled로 안됨

2. non-signaled 상태에서는 Set() 호출하면 signaled로 됨





AutoResetEvent 클래스


http://www.csharpstudy.com/Threads/autoresetevent.aspx



신호(Signal)에 의한 쓰레드 동기화 

쓰레드 동기화에는 Locking과 같이 공유 리소스에 락을 걸어 쓰레드 접근을 제한하는 방식 (Monitor, Mutex, Semaphore 등)이외에 대기 중인 쓰레드에게 외부에서 신호(Signal)을 보냄으로써 쓰레드 흐름을 통제하는 방식도 있다. 이러한 신호(Signaling) 방식으로 많이 사용되는 것으로 AutoResetEvent, ManualResetEvent, CountdownEvent, Wait/Pulse 등이 있다. (NOTE: 여기서의 Event는 윈도우 프로그래밍에서 말하는 이벤트와 전혀 다른 개념이다. 여기서의 Event는 쓰레드 동기화에 사용되는 OS 리소스이다) 



AutoResetEvent 클래스 

AutoResetEvent는 이 이벤트를 기다리는 쓰레드들에게 신호를 보내 하나의 쓰레드만 통과시키고 나머지 쓰레드들은 다음 신호를 기다리게 한다. 이는 흡사 유료 주차장 자동 게이트와 같이 한 차량이 통과하면 자동으로 게이트가 닫히는 것과 같다. 쓰레드 A가 AutoResetEvent 객체의 WaitOne() 메소드를 써서 대기하고 있다가, 다른 쓰레드 B에서 이 AutoResetEvent 객체의 Set() 메서드를 호출하면, 쓰레드 A는 대기 상태를 해제하고 계속 다음 문장을 실행할 수 있게 된다. 

예제

using System;
using System.Threading;

namespace MultiThrdApp
{
    class Program
    {
        // AutoResetEvent 객체 필드
        static AutoResetEvent autoEvent = new AutoResetEvent(false);

        static void Main()
        {
            // 쓰레드 A 생성
            Thread A = new Thread(Run);
            A.Name = "Thread A";
            A.Start();            

            // 메인쓰레드            
            Thread.Sleep(3000); //3초 대기
            autoEvent.Set(); // 쓰레드 A에 신호
        }

        static void Run()
        {
            string name = Thread.CurrentThread.Name;
            Console.WriteLine("{0}: Run Start", name);

            // AutoResetEvent 신호 대기
            autoEvent.WaitOne();            
            Console.WriteLine("{0} : DoWork", name);

            Console.WriteLine("{0}: Run End", name);
        }
    }
}




AutoResetEvent 활용 예제 

AutoResetEvent 활용한 한 예제로 교통 신호를 통제하는 예를 들어 보자. 실제 교통 신호보다 좀 단순화하여 상하방향과 좌우방향만을 각각 통제하는 2개의 신호만이 있다고 가정하자. 상하방향 신호가 켜지면 상하방향 차량들이 모두 통과되고 (즉, 상하방향 Queue에 있는 모두 데이타 처리), 더 이상 차량이 없으면 좌우방향에 신호를 보내 좌우방향에 밀려 있는 차량들을 통과한다 (즉, 좌우방향 Queue에 있는 모두 데이타 처리). 아래 예제는 2개의 쓰레드가 각각 상하, 좌우 데이타 처리를 위한 메서드들을 실행하고, 2개의 AutoResetEvent 객체를 신호로 사용하고 있다. 각 쓰레드의 메서드는 먼저 자신의 신호 이벤트를 기다렸다가 이벤트가 Set되면 큐의 있는 모든 데이타를 처리한 후 상대편 이벤트를 Set하여 상대편에게 실행 제어권을 넘긴다. 메인쓰레드에서는 계속 데이타를 큐에 넣는 역활을 하고 있다. 

예제

using System;
using System.Threading;
using System.Collections.Generic;

namespace MultiThrdApp
{
    class Program
    {
        static void Main()
        {
            Traffic traffic = new Traffic();

            // 2개의 쓰레드 구동
            Thread v = new Thread(traffic.ProcessVertical);
            Thread h = new Thread(traffic.ProcessHorizontal);
            v.Start();
            h.Start();

            // 메인쓰레드에서 데이타 전송
            for (int i = 0; i < 30; i+=3)
            {
                traffic.AddVertical(new int[] { i, i + 1, i + 2 });
                traffic.AddHorizontal(new int[] { i, i + 1, i + 2 });
                Thread.Sleep(10);
            }

            Thread.Sleep(1000);
            traffic.Running = false;                 
        }
    }

    class Traffic
    {
        private bool _running = true;

        // 상하, 좌우 통행 신호 역활을 하는 AutoResetEvent 이벤트들
        private AutoResetEvent _evtVert = new AutoResetEvent(true);
        private AutoResetEvent _evtHoriz = new AutoResetEvent(false);

        private Queue<int> _Qvert = new Queue<int>();
        private Queue<int> _Qhoriz = new Queue<int>();

        // 상하방향의 큐 데이타 처리
        // Vertical 방향의 처리 신호(_evtVert)를 받으면
        // Vertical 큐의 모든 큐 아이템을 처리하고
        // 좌우방향 처리 신호(_evtHoriz)를 보냄
        public void ProcessVertical()
        {            
            while (_running)
            {
                // Vertical 방향 처리 신호 기다림
                _evtVert.WaitOne();

                // Vertical 큐의 모든 데이타 처리
                // 큐는 다른 쓰레드에서 엑세스 가능하므로 lock을 건다
                lock (_Qvert)
                {
                    while (_Qvert.Count > 0)
                    {
                        int val = _Qvert.Dequeue();
                        Console.WriteLine("Vertical : {0}", val);
                    }
                }

                // Horizontal 방향 처리 신호 보냄
                _evtHoriz.Set();
            }

            Console.WriteLine("ProcessVertical : Done");
        }

        // 좌우방향의 큐 데이타 처리
        // Horizontal 방향의 처리 신호(_evtHoriz)를 받으면
        // Horizontal 큐의 모든 큐 아이템을 처리하고
        // 상하방향 처리 신호(_evtHoriz)를 보냄
        public void ProcessHorizontal()
        {
            while (_running)
            {
                _evtHoriz.WaitOne();

                lock (_Qhoriz)
                {
                    while (_Qhoriz.Count > 0)
                    {
                        int val = _Qhoriz.Dequeue();
                        Console.WriteLine("Horizontal : {0}", val);
                    }
                }
                
                _evtVert.Set();
            }

            Console.WriteLine("ProcessHorizontal : Done");
        }

        public bool Running
        {
            get { return _running; }
            set { _running = value; }
        }

        public void AddVertical(int[] data)
        {
            lock (_Qvert)
            {
                foreach (var item in data)
                {
                    _Qvert.Enqueue(item);
                }                
            }
        }

        public void AddHorizontal(int[] data)
        {
            lock (_Qhoriz)
            {
                foreach (var item in data)
                {
                    _Qhoriz.Enqueue(item);    
                }                
            }
        }
    }
}








ManualResetEvent 클래스


http://www.csharpstudy.com/Threads/manualresetevent.aspx



ManualResetEvent 클래스 

ManualResetEvent는 하나의 쓰레드만 통과시키고 닫는 AutoResetEvent와 달리, 한번 열리면 대기중이던 모든 쓰레드를 실행하게 하고 코드에서 수동으로 Reset()을 호출하여 문을 닫고 이후 도착한 쓰레드들을 다시 대기토록 한다. 아래는 여러 쓰레드의 실행을 중지시킨 후, ManualResetEvent로 신호를 보내 대기중이던 모든 쓰레들들을 한꺼번에 실행시키는 예제이다. 

예제

using System;
using System.Threading;

namespace MultiThrdApp
{
    class Program
    {
        // ManualResetEvent 객체 필드
        static ManualResetEvent manualEvent = new ManualResetEvent(false);

        static void Main()
        {
            // 10개의 쓰레드 생성
            // 10개 쓰레드 모두 manualEvent.WaitOne(); 에서
            // 실행 중지후 대기중
            for (int i = 0; i < 10; i++)
            {
                new Thread(Run).Start(i);
            }

            // 메인쓰레드            
            Thread.Sleep(3000);

            // ManualResetEvent 객체 Set() 호출
            // 10개 쓰레드 모두 실행 계속함.
            manualEvent.Set(); 
        }

        static void Run(object id)
        {            
            Console.WriteLine("{0} in Wait", id);

            // ManualResetEvent 신호 대기
            manualEvent.WaitOne();                        

            Console.WriteLine("{0}: Done", id);
        }
    }
}



테스드 결과 







CountdownEvent 클래스


http://www.csharpstudy.com/Threads/countdownevent.aspx



ManualResetEvent가 한 쓰레드에서 신호(Signal)을 보내 복수 쓰레드들을 통제하는데 사용되는 반면, .NET 4.0에 소개된 CountdownEvent는 한 쓰레드에서 복수 쓰레드들로부터의 신호들을 기다리는데 사용된다. 아래는 10개의 쓰레드를 시작한 후, 이 쓰레드들로부터 처음 5개의 신호가 (CountdownEvent.Signal() 메서드) 먼저 도착하는 대로 메인쓰레드는 Wait 대기 상태를 해제하고 다음 문장을 실행하게 된다. 

예제

using System;
using System.Threading;

namespace MultiThrdApp
{
    class Program
    {
        // CountdownEvent 객체 필드
        static CountdownEvent countEvent = new CountdownEvent(5);

        static void Main()
        {
            // 10개의 쓰레드 시작
            // 10개중 5개만 Vote만 끝내면 중지            
            for (int i = 0; i < 10; i++)
            {
                new Thread(Vote).Start(i);
            }

            // 메인쓰레드 첫 5개 신호를 기다림
            countEvent.Wait();

            Console.WriteLine("Vote is done!");            
        }

        static void Vote(object id)
        {                        
            if (countEvent.CurrentCount > 0)
            {
                // CountdownEvent 신호. -1씩 카운트다운.
                countEvent.Signal();

                Console.WriteLine("{0}: Vote", id);
            }
            else
            {
                Console.WriteLine("{0}: No vote", id);
            }
        }
    }
}
















'프로그래밍 > C#' 카테고리의 다른 글

Events  (0) 2017.03.29
Common I/O Tasks  (0) 2017.03.15
Sockets  (0) 2017.03.02
Using the Visual Studio Development Environment for C#  (0) 2017.02.14
Getting Started with C#  (0) 2017.02.13
:
Posted by 지훈2