LISTORY

[윈도우즈 시스템 프로그래밍] 쓰레드의 성격과 특성 본문

IT/윈도우 프로그래밍

[윈도우즈 시스템 프로그래밍] 쓰레드의 성격과 특성

LiStoryTeller 2018. 8. 11. 19:12


뇌를 자극하는 윈도우즈 시스템 프로그래밍 책 관련 YOUTUBE 강의 정리이다.


이번에 정리할 내용은  쓰레드의 성격과 특성이다.


YouTube 강의 주소  쓰레드의 성격과 특성



쓰레드의 성격과 특성


하나의 프로세스 안의 모든 쓰레드들은 stack을 독립적으로 갖고, 힙, 데이터, 코드 영역을 공유한다.


즉, 하나의 프로세스안에 하나의 함수, 변수등을 선언해 두고 여러 쓰레드에서 사용하다는 것이 가능하다는 뜻이다.


그렇다면 동시에 여러 쓰레드가 하나의 변수에 접근하면 어떻게 될까?



⊙ 동시접근의 문제


이런 경우를 생각해보자


A, B 쓰레드가 둘다 RUNNING 중이고, 같은 변수(total)를 사용한다.


A가 작업중이라고 가정해보자.



계산을 위해 레지스터 r1을 사용한다. 


계산을 하여 r1에 16이 저장되었고, 이를 변수 total에 갱신해야 한다.


근데 이 상황에서 쓰레드 간에 컨텍스트 스위칭이 발생하였다.


그럼 일단 작업을 멈춰야 하는데, 레지스터 정보는 실행의 대상이 바뀌면 저장을 해야한다.


그래서 r1의 값은 메모리에 잠시 저장이 된다.


그런 다음 쓰레드 B가 저장할 수 있도록 레지스터를 비워진다.



그럼 이제 B 쓰레드가 작업을 하고, r1에 19가 저장된다.


그런 다음 다시 컨텍스트 스위칭이 일어났다.


그럼 임시적으로 저장된 데이터를 다시 r1으로 불러들인다.


그래서 total 값은 다시 16이 되버린다.


이러한 문제를 메모리의 동시 접근 문제라고 한다.


이 문제를 막기 위해선, 하나의 쓰레드가 total에 작업을 할때 다른 쓰레드는 total에 접근할 수 없도록 해야한다.


이런걸 임계 영역이라 하는데, 이는 메모리 공간을 말하는 것이 아니라


코드 블럭, 둘 이상의 쓰레드가 동시에 실행되면 안되는 부분을 뜻한다.



⊙ 프로세스로부터의 쓰레드 분리



프로세스가 생성되면 UC가 2가 되는 것처럼, 쓰레도도 마찬가지로 생성과 동시에 UC가 2가 된다.


* 왜 UC가 2가 될까?


쓰레드의 실행을 위해 +1,


쓰레드의 생성을 요청한 프로세스, 또는 다른 쓰레드에게 핸들을 반환하면서 +1


즉, 쓰레드의 커널 오브젝트에 접근할 수 있는 것은 두개가 되는 것이다.


이로 인해 문제가 생길 수 있다.


예를 들어 A라는 프로세스가 B라는 쓰레드를 생성했다고 해보자.


B라는 쓰레드가 자신의 일을 하고 종료되고, 그럼 UC는 1이 된다.


즉, 쓰레드의 커널 오브젝트는 종료되지 않고 남아있다.


이런 식으로 쓰레드를 계속 생성하면 커널 오브젝트가 메모리에 계속 누적되는 문제가 발생할 수 있다.


그럼 쓰레드가 종료하자마자 커널 오브젝트를 종료할 수 있도록 해야하는데,


이를 위해 A 프로세스가 CloseHandle() 함수를 호출해서 uc 하나 감소해야한다.


따라서 쓰레드는 생성과 동시에 CloseHandle() 함수를 호출하여 쓰레드를 분리해야한다.


이걸 프로세스로부터 쓰레드를 분리한다고 한다.


이 때 주의해야 할 점은 쓰레드의 종료 코드를 커널 오브젝트에 저장해두면 안된다는 것이다.


쓰레드의 종료 코드를 커널 오브젝트에 저장해두고 그걸 읽어들이고 싶을 경우엔 


CloseHandle() 함수는 종료 코드를 얻고 나서 해야한다.



ANSI 표준 C 라이브러리와 쓰레드


CreateThread, ExitThread


_beginthreadex, _endthreadex


초창기에는 쓰레드를 많이 사용하지 않았기 때문에 쓰레드 임계영역을 생각하지 않고 만든 코드가 많다.


그래서 CreateThread를 사용할 경우, 메모리를 동시에 사용하여 안전하지 않은 경우가 많다.


_beginthreadex는 내부적으로 CreateThread를 사용하지만, 하나의 메모리 공간에 동시 접근하지 하도록 쓰레드 별로 별도의 공간을 만들어 준다.


즉, _beginthreadex로 쓰레드를 생성 시, 쓰레드 별로 별도의 메모리 공간을 할당해 준다. 


_beginthreadex로 만든 쓰레드는 _beginthreadex 호출 시 할당된 메모리 공간을 반환하기 위해서 _endthreadex를 호출하여 종료해 주어야 한다.



Comments