2015. 1. 26.

[OS]쓰레드(Thread)와 Critical Section(임계영역)

 쓰레드에 대한 기본적인 지식을 바탕으로 쓰레드의 특성에 대해 좀더 살펴보고자 한다. 메모리 관점에서 본 쓰레드에 대한 지식이 있다면 이해하기 편할 것이다.


프로세스와 메인 쓰레드(main thread)

  쓰레드는 프로세스에 존재하는 실행 흐름 중 하나이다. 그런 실행 흐름 중에는 우리가 흔히 main() 함수라고 부르는 쓰레드가 존재한다. 어떤 프로그램을 실행시키면 main() 함수가 실행되는데, 이는 다시 말해 main 쓰레드가 실행된다는 것을 의미한다.  만약 main 쓰레드를 통해서 다른 쓰레드를 만들어서 다른 쓰레드로 하여금 특정한 일을 시키면 어떨까? 이 상황은 아래의 그림과 같다.(쓰레드의 의미 중에 실, 가닥이라는 의미도 있다)

<출처: http://www.onlinebuff.com/article_understand-threading-and-types-of-threading-in-c-using-an-example_56.html>

 main 쓰레드는 프로세스가 실행되면 자동적으로 실행되는 thread이다. 그림에 등장하는 worker 쓰레드는 main 쓰레드가 생성한 새로운 쓰레드이다. 새로운 worker 쓰레드는 main 쓰레드와는 별개로 자신의 고유한 작업 흐름을 가진다.

 그런데 메모리 관점에서 본 쓰레드를 떠올려 보면 쓰레드는 heap, data 영역을 공유한다는 것을 알 수 있다. 고유한 작업 흐름을 가지는데 메모리를 공유한다는 점에 주목해야 한다.


쓰레드를 사용하여 실행흐름을 분할 

 만약 어떤 사람이 0 ~ 100,000 사이의 숫자의 총합을 구하고자 한다. 이 사람은 thread의 대한 지식을 가지고 있었다. 이 사람은 0 ~ 33,333까지의 합을 thread1로 처리하고, 33,334 ~ 66,666까지의 합은 thread2로 처리하고, 66,667 ~ 100,000가지의 합은 thread3으로 처리해야겠다고 생각하였다. 하나의 thread로 0 ~ 100,000까지의 합을 구하는 것보다는 실행흐름을 3개로 나누고, 3개의 실행 흐름을 병렬적으로 수행하면 합을 구하는 시간이 줄어들 것으로 예상한 것이다. 얼핏 들으면 매우 그럴듯한 발상이다. 하지만 이를 그대로 코드로 옮기면 문제가 발생한다.


 ThreadProc() 함수 바로 앞에 선언된 total은 static 변수이다. static 변수는 메모리의 data 영역에 존재하고, thread는 이를 공유하게 된다. 결국 3개의 쓰레드는 모두 static 변수 total에 접근가능하다. 이런 특성때문에 쓰레드는 IPC와 같은 방법으로 통신할 필요가 없다.

 하지만 위 total 변수는 쓰레드의 동시접근 문제를 발생시킬 수 있다.

 쓰레드는 결국 스케줄러에 의해 번갈아가며 실행된다. 이 과정에서 컨텍스트 스위칭은 불가피하게 발생한다. 프로세스는 독립적인 메모리 공간을 가지기 때문에 컨텍스트 스위칭 과정에서 데이터가 변경되지는 않는다. 하지만 쓰레드 간의 컨텍스트 스위칭은 상황이 다르다. 컨텍스트 스위칭 상황에서 쓰레드는 프로그램 라인 단위로 이뤄지지 않는다. 예를 들어 A() 함수가 실행 중 일 때는 절대 컨텍스트 스위칭이 발생하지 않는다고 생각하면 안 된다. 실행 중인 쓰레드의 변경은 매우 빈번하게 발생하며, 우리가 이에 쓰레드의 실행 흐름을 예측해서 제어하는 것은 불가능하다.

 그래서 둘 이상의 쓰레드가 위의 total과 같이 같은 메모리 영역을 동시에 참조하면 문제를 일으킬 수 있다. 우리는 덧셈이 올바르게 완료될 것으로 예상하지만, 이는 우리의 기대일 뿐이다. 1 + 2의 값을 3이라고 저장하지도 못한 상태에서 33,334 + 33,335를 계산하는 쓰레드가 실행될지도 모른다. 이렇게 되면 결국 total의 값은 3만큼 누락된다.


임계영역(Critical Section)

 위 코드의 worker 쓰레드 함수 내부의 total을 둘 이상의 쓰레드가 동시에 실행할 경우 문제가 발생할 수 있다. 이런 문제를 일으킬 수 있는 코드 블럭을 임계영역이라고 한다. 다시 말해 임계영역은 배타적 접근(한 순간에 하나의 쓰레드만 접근)이 요구되는 공유 리소스(전역변수, static 변수 등)에 접근하는 코드 블럭을 의미한다.

댓글 없음:

댓글 쓰기