본문 바로가기
Programing_Language/C#

[쓰레드 동기화] Interrupt(이벤트) 방식의 AutoResetEvent

by neohtux 2020. 7. 21.
728x90

스핀 락(Spin lock)처럼 폴링 방식의 스레드 대기는 CPU 점유율이 올라갈 수 있다. 

무한정 대기상태라는 말은 계속 잠금이 풀릴때까지 확인하는 방법이다. 


스레드가 무한정 대기하는 부담을 줄일 수 있는 방법이 있다. 

하지만, 이것은 커널단의 호출로 문맥교환의 오버헤드가 발생된다.

더보기


마이크로 프로세서에서의 단일코어 단일 프로세스 예를 들어보면,

폴링 방식으로 어떠한 값의 state 변화를 감시하고 계속 대기 시켜놓으면
그 일만 하며 무한정 대기한다.


하지만, 내부 혹은 외부인터럽트를 사용하면,

코드를 한줄 한줄 실행 중간에 들어온 인터럽트를 확인하고,

현재 작업을 중단 시킨다. 


작업을 중단시키는 것은 아얘 작업을 비우는 것이아닌 레지스터와 내부 메모리의
어떤 지점에 현재 작업정보와 다음 작업해야할 정보를 임시 저장해 놓는다.


이 과정 에서 운영체제의 PCB (프로세스 제어 블록) 과 유사한 역할을 하는

MCU의 레지스터들이 있다. 똑같이 프로그램 카운터(PC)와 스택 포인터(SP) 레지스터

메모리 어드레스 레지스터(MAR) 등등 이 있다.



non_OS(운영체제) 입장의 MCU에서는 현재 상태를 레지스터들에 모두 저장을 해놓고
내부 인터럽트 벡터 번호를 확인하고 해당 인터럽트가 실행될 콜백함수의 주소를 실행한다.

그 함수가 종료되면, 다시 PC와 스택포인터, 그리고 사용중이던 자원들과 주소들을
데이터 레지스터, 주소 레지스터 등 다양한 레지스터를 활용하여 가져온다.


이런 일들이 1번 일어 나는것은 프로세서 입장에서는 굉장히 빠르므로 사람이 느끼기 어렵다.


하지만 이런 일들이 10만번 100만번 일어났을 경우 운영체제에서의 오버헤드는 

사람이 느낄 수 있을 정도이다.

그럼 인터럽트처럼 Event를 발생시켜 해당 잠금(lock)을 사용할 수 있는 상태가 되면 스레드한테 알려주면 어떨까?

이러면 CPU는 바쁜 대기(busy waiting)을 하지 않아도된다. 즉 while 문에서 계속 잠금을 사용할 수 있는지에 대한

부하를 줄일 수 있다.

 

Class : AutoResetEvent   Method ={Waitone() , Set() }

 

AutoResetEvent ARE= new AutoResetEvent (bool)

parameter 1 : true 상태이면 사용 가능상태, false이면 잠김 상태, 초기 값을 설정해준다.

 

WaitOne(), 입장시도를 한다. (만약, 입장 가능상태이면 잠금 상태를 변화시키고 작업을 진행)
WaitOne을 사용하여 메소드를 써서 대기하고 있다가, 다른 스레드에서 Set() 메소드를 실행시키면
이벤트를 발생시켜 다른 스레드들에게 해당 공유 자원에 접근 허용상태를 알려주는 것이다.

 class Lock
    {
        AutoResetEvent ARE = new AutoResetEvent(true); //true available, false lock;
        //ManualResetEvent MRE = new ManualResetEvent(true); //수동으로 문닫기
        public void Acquire()
        {
            ARE.WaitOne(); //입장 시도
          //  _available.Reset(); // bool =false; //문을 닫는다
        }
        public void Release()
        {
            ARE.Set(); //bool = true; 문을 연다.
        }
    }

    class Program
    {
        static int num = 0;
        static Lock _lock = new Lock();
        //static Mutex _lock = new Mutex()
        static void Thread_1()
        {
            for(int i=0; i<10000; ++i)
            {
                //_lock.WaitOne(); // (locked == true);
                _lock.Acquire();
                num += 1;
                //_lock.ReleaseMutex(); // (locked == false) lock 해제
                _lock.Release();
            }
        }
        static void Thread_2()
        {
           for(int i=0; i<10000;++i)
            {
                _lock.Acquire();
                num -= 1;
                _lock.Release();
            }
        }
       
        static void Main(string[] args)
        {
             Task t1 = new Task(Thread_1);
             Task t2 = new Task(Thread_2);

             t1.Start();
             t2.Start();
             Task.WaitAll(t1, t2);
    
            Console.WriteLine(num); //0인지 확인
        }
    }

하지만, 실제 10만번을 수행하면 문맥교환이 일어남에 따라 
스핀 락이나 , 임의 접근(임의 대기, 조건부 양보)보다 느리다는것을 알 수 있다.
1만번을 하면 10만번보다 빠르게 값이 동기화 되는것을 볼 수 있다.

300x250

댓글