본문 바로가기
programming | development/c#

Thread ,lock, Monitor

by foooo828 2022. 1. 9.

프로세스? 실행파일이 실행되어 메모리에 적재된 인스턴스

스레드? 운영체제가 CPU 에 시간을 할당하는 기본단위

 

멀티스레드 사용시 장점

- 높은 응답성

- 멀티프로세스 방식에 비해 멀티스레드 방식이 자원 공유가 쉬움.  코드 내 변수를 같이 사용하는것으로 데이터 공유 가능

- 이미 프로세스에 할당된 메모리 자원을 그대로 사용하므로 자원 할당 비용 지불하지 않아도 됨

 

단점

- 디버깅 

- 자식스레드 하나 문제시 전체 프로세스 영향

- 너무 많은 스레드 사용시 작업간 전환이 비용을 많이 소모하므로 성능저하

 

스레드 상태변화

- Unstarted : 스레드 객체 생성 후 Start() 호출 전

- Running : 스레드 동작중

- Suspended : 스레드 일시 중단. Thread.Suspend()로 상태 돌입, Thread.Resume() 로 다시 동작

- WaitSleepJoin :  스레드가 Block 된 상태. THread.Enter(),Thread.Sleep(), Thread.Join() 호출시 상태 돌입

- Aboarted : 스레드 취소상태

- Stopped  : 중지된 상태

- Background : 스레드가 백그라운드로 동작하고 있음. Foreground 는 스레드가 하나라도 살아있으면 프로세스가 죽지 않지만 백그라운드는 열개가 살아있어도 프로세스에 영향x

 

ThreadState Flags attribute 

비트필드?   구조체를 선언할 때 바이트 단위가 아닌 비트 단위로 선언한 필드. 주로 비트 단위의 플래그를 표현하기 위해 사용.

스레드는 동시에 두가지 이상의 상태를 가질 수 있기때문에 그것들을 동시에 표현하기 위해서 ThreadState 열거형 멤버는 Flags attribute 를 가지고있음

비트연산을 통해 상태를 알아낼수 있음

 

 // ThreadState 필드 값 확인 예제
 if(t1.ThreadState & ThreadState.Aborted == ThreadState.Aborted)

 

Thread.Interrupt()

스레드가 WaitJoinSleep 상태일때 호출하면 즉시 ThreadInterruptedExeption 발생. 중지

스레드가 Running 상태일때는 나중에  WaitJoinSleep 상태일때 ThreadInterruptedExeption 발생. 중지

 

Thread.Aboart()

호출시 ThreadAboartExeption 발생. 예외 처리후 finally 블록 실행 후에 스레드 종료

 

Thread.Sleep()

다른 스레드도  CPU 를 사용할 수 있도록 CPU 점유를 내려놓음

 

Thread.SpinWait()

Sleep()과 유사

스레드를 대기하지만 Running 상태 유지

 

lock 키워드

동기화?   스레드 끼리는 자원들을(파일 핸들이나 네트워크 커넥션, 메모리에 선언한 변수등) 공유하는데 이때 스레드끼리 한번에 하나씩 순서를 가지고 자원을 사용하게 하는것을 동기화 라고 함. 이때 사용하는것이 lock 키워드와 Monitor 클래스

lock 키워드로 critical section(한번에 한 스레드만 사용할 수 있는 코드 영역)으로 설정하고  lock 을 획득한 스레드만 크리트컬 섹션에 접근 가능하도록 함

class Counter
    {
        public int count = 0;
        private readonly object _thisLock = new object();

        public void Increase()
        {
            lock (_thisLock)
            {
            // 크리티컬 섹션
                count++;
            }
        }
    }

크리티컬 섹션은 반드시 필요한 곳에(가능한 범위를 작게) 사용해야함.

lock 의 매게변수에는 주로 private필드 obect타입을 사용해야함

 

lock 의 매개변수로 외부 접근가능한(public)필드 사용시 주의사항

- this : 클래스 객체를 의미하는 키워드. 객체 전체범위에서 lock 을 걸면 Lock Granularity(잠금단위) 를 떨어뜨리고 DeadLock(다른 스레드가 락이 해제될때까지 대기) 하는 부작용 발생

- Type 형식:  typeof 연산자나 GetType() 메소드는 Type 형식의 인스턴스를 반환하기 때문에 코드의 어느곳에서나 특정 형식에 대한 Type 객체를 얻을 수 있으므로 피해야됨

- string 형식 : string 객체는 어느 코드에서든 얻어낼수 있으므로 쓰면 안됨

 

Monitor 클래스 

Monitor.Enter() : 크리티컬 섹션 만들기  (중괄호   {  )

Monitor.Extit() : 크리티컬 섹션 제거  (중괄호   }  )

lock 키워드와 똑같은 기능이며 lock 이  Monitor의  Enter() 과 Exit() 바탕으로 구현됨

class Counter
{
    private const int Loop_Count = 1000;
    private readonly object thisLock;

    private int count;
    public int Count => count;
    public Counter()
    {
        thisLock = new object();
        count = 0;
    }

    public void Increase()
    {
        int loopCount = Loop_Count;
        while (loopCount-- > 0)
        {
            Monitor.Enter(thisLock);
            try
            {
                count++;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            finally
            {
                Monitor.Exit(thisLock);
            }

            Thread.Sleep(1);
        }
    }
}

 

Monitor.Wait()  Monitor.Pulse()

lock 블록 내에서 호출해야함. 

Wait() Pulse() 호출할때 일어나는 일

 

Pulse  호출시 뒤에 대기중인 스레드가 없으면 Pulse 신호는 없어짐

 

Thread.Sleep 과 Monitor.Wait 의 차이?  둘다 스레드를 WailtSleepJoin 상태로 만들지만

- Thread.Sleep:  Monitor.Pulse()에의해 깨어날 수 없고 Wating Queue 에도 들어가지 않음.

   다시 Running 으로 돌아오기 위해서는 매개변수로 입력한 시간이 경과되거나 Interrupt() 호출에의한 예외를 받아야함

- Monitor.Wait:  Pulse() 호출시 바로 깨어남

 

 // 스레드가 블록될 조건검사용
private bool lockedCount = false;

public void Increase()
        {
            int loopCount = Loop_Count;
            while (loopCount-- > 0)
            {
                Monitor.Enter(thisLock);
                try
                {
                    while (count > 0 || lockedCount == true)
                    {
                        Monitor.Wait(thisLock);
                    }

                    lockedCount = true;
                    count++;
                    lockedCount = false;

                    Monitor.Pulse(thisLock);
                }
                finally
                {
                    Monitor.Exit(thisLock);
                }
                Thread.Sleep(1);
            }
        }

 

 

 

참고 :

이것이 C# 이다

https://www.csharpstudy.com/Threads/lock.aspx

 

 

'programming | development > c#' 카테고리의 다른 글

Task , Parallel, async, await  (0) 2022.01.10

댓글