나의 지식 보관소
프로세스와 스레드 / 동기화 본문
프로세스: 실행 파일이 실행되어 메모리에 적재된 인스턴스, 하나 이상의 스레드로 구성된다.
스레드: 운영체제가 CPU시간을 할당하는 기본 단위. 여러개가 있으면 멀티스레드로 불린다.
멀티스레드의 장점:
1. 사용자 대화형 프로그램에서 응답률을 높힐 수 있다.
2. 멀티프로세스에 비해 자원 공유가 쉽다.
3. 경제성이 높다.
멀티스레드의 단점:
1. 멀티스레드 프로그램의 구현이 어렵다.
2. 자식스레드에 문제가 생기면 전체 프로세스가 영향을 받는다.
3. 스레드가 너무 많이 사용되면 성능이 저하된다.
스레드 시작하기
.NET 프레임워크는 스레드를 나타내는 클래스로 System.Threading.Thread를 제공하므로 이것을 사용하면 된다.
1
2
3
4
5
|
Thread t1 =
new Thread(new ThreadStart(Work));//스레드가 실행할때 호출될 메서드를 초기화해준다
//( "Work" 원하는 메서드명으로 바꾸면 된다. )
t1.Start();//스레드를 시작한다
t1.Join();//스레드의 종료를 기다린다
|
스레드를 임의로 종료시키기
살아있는 스레드를 죽이려면 Thread객체의 Abort()메서드를 사용하면된다.
Abort()메서드가 호출되면 CLR은 해당 스레드가 실행중이던 코드에 ThreadAbortException을 호출한다.
단, 프로그래머들은 abort()메서드를 사용하는것을 금기시하는데 그 이유는 어떤 스레드에서 하나의 자원을 독점하고자 잠근후 그 자원을 해제하지 못한채 죽어버리면, 그 자원에 접근하기 까다로워져버리기 때문이다.
또 다른 스레드를 멈추는 방법은 인터럽트가 있다. Thread.Interrupt() 메서드는 스레드가 한창 동작중인 상태( Running 상태 ) 를 피해서 WaitJoinSleep 상태에 들어갔을 때 ThreadIntteruptedException 예외를 던져 스레드를 종료시킨다.
스레드의 상태
상태 | 설명 |
Unstarted | 스레드 객체를 생성한 후 Thread.Start 메서드가 호출되기 전의 상태 |
Running | 스레드가 시작하여 동작 중인 상태. |
Suspended | 스레드의 일시 중단 상태, Thread.Suspend() 메서드를 통해 이 상태로 만들수있고 Thread.Resume() 메서드를 통해 다시 Running 상태로 되돌릴수 있다. |
WaitSleepJoin | 스레드가 Block된 상태, Monitor.Enter(), Thread.Sleep(), Thread.Join() 메서드를 통해 이 상태가 된다. |
Aborted | 스레드가 취소된 상태, 이 상태의 스레드는 곧 Stopped 상태로 전환 되어 완전히 중지된다. |
Stopped | 중지된 스레드의 상태, |
Background | 스레드가 백그라운드로 동작하고 있음을 나타낸다. Foreground 스레드가 하나라도 살아있는 한 프로세서는 죽지 않지만 Background는 열개가 살아 있어도 프로그램이 살고 죽는 것에 영향을 주지 않는다. 단, 프로세서가 죽으면 모든 백그라운드 스레드도 죽는다. Thread.IsBackground 속성에 true를 줌으로써 이상태로 만들수 있다. |
스레드의 동기화
스레드들이 순서를 갖춰 자원을 한번에 하나의 스레드가 사용하도록 보장 하는 것을 동기화라고 하는데, 이것을 해주지 않으면 하나의 자원에 여러 스레드들이 동시에 작업을 하면서 원치 않은 결과가 나타난다.
.NET 프레임워크가 동기화를 위해 제공하는 대표적인 도구로 lock 키워드와 Monitor 클래스가 있다.
lock 키워드로 동기화 하기
C#에서 lock 키워드로 감싸주기만 하면 해당 부분을 크리티컬 섹션(critical section)으로 바꿀 수 있다. 크리티컬 섹션이란 한번에 한 스레드만 사용할 수 있는 코드 영역을 뜻한다.
1
2
3
4
|
lock(thisLock)
{
count = count + 1;
}
|
위 코드를 보면 lock 키워드에는 매개변수가 필요하다는 것을 알 수 있다. lock키워드에서 매개변수는 해당 주소값을 가지고 있는 참조형변수를 열쇠로써 사용한다는 의미이다. 즉 다른 스레드가 먼저 lock문에 들어가면서 열쇠를 써버리면 심지어 다른곳에 있는 lock문도 다른 스레드에서는 접근할수없다. 말그대로 그 열쇠를 사용하는 모든 lock문이 잠긴다. 따라서 lock("abc")와 같은 코드는 절대 피하여야 한다.
Monitor 클래스로 동기화하기
Monitor 클래스는 동기화에 사용하는 몇가지 정적 메서드를 제공한다. 그 중 가장 중요한것은 Monitor.Enter()과 Monitor.Exit()메서드이다. Monitor.Enter()메서드는 크리티컬 섹션을 생성하고 Monitor.Exit()는 크리티컬 섹션을 나간다. 즉 각각 lock키워드의 {와 }에 대응한다고 보면된다. 다음코드는 Monitor.Enter()와 Monitor.Exit()로 동기화를 하는 코드이다.
1
2
3
4
5
6
7
8
9
|
Monitor.Enter(thisLock);
try
{
count = count + 1;
}
finally
{
Monitor.Exit(thisLock);
}
|
Monitor.Wait()와 Monitor.Pulse를 통한 저수준 동기화
사실 Monitor.Enter()와 Monitor.Exit()는 lock 키워드와 완전 동일하기 때문에 Monitor 클래스를 사용해야 하는 일이 생긴다면 Monitor.Wait()와 Monitor.Pulse() 를 사용하기 위함 일 것이다. 이 두메서드는 반드시 lock 블럭 안에서 호출되어야 한다.
Monitor.Wait() 메서드는 스레드를 WaitSleepJoin 상태로 만든다. 이렇게 WaitSleepJoin 상태로 들어간 스레드는 동기화를 위해 갖고 있던 lock을 내려 놓은뒤 Wating Queue라고 하는 큐에 입력된 뒤ㅡ 다른 스레드가 lock을 얻어 작업을 수행한다. 작업을 수행하던 스레드가 일을 마친뒤 Pulse() 메서드를 호출하면 CLR은 Waiting Queue큐의 가장 첫 요소 스레드를 꺼낸뒤 Ready Queue에 입력시킨다. 그 후 Ready Queue에 입력된 스레드는 입력된 차례에 따라 lock을 얻어 Running 상태에 들어간다. 즉 다시 작업을 수행한다.
단, Thread.Sleep() 메서드에 의해 WaitSleepJoint 상태가 된 스레드는 Monitor.Pulse()메서드에 의해 깨어날수 없다.
'프로그래밍 언어 > C#' 카테고리의 다른 글
이벤트 event (0) | 2020.05.05 |
---|---|
대리자 (0) | 2020.05.05 |
제네릭(일반화) / Generic (0) | 2020.04.22 |
가변 배열 ( Jagged Array ) (0) | 2020.04.22 |
무명 형식 (0) | 2020.04.21 |