1. Windows에서의 쓰레드 생성과 소멸

* 쓰레드의 생성

: 쓰레드를 생성할 수 있는 최대 개수는 메모리가 허용하는 만큼이다.

: 쓰레드의 흐름은 예측할 수 없다. 누가 먼저 실행될 것인지 예측하는 것은 의미가 없다.

: Sleep 함수 -> 자신에게 현재 할당된 타임슬라이스를 포기하고 해당 시간동안 우선순위가 같은 다른 쓰레드에게 실행의 기회를 양보한다.

* 쓰레드의 소멸

: 쓰레드 함수 내에서 return으로 소멸시키는 것이 가장 이상적이다.

case 1 : 쓰레드 종료시 return을 이용하면 좋은 경우(거의 대부분의 경우)

: 1~10까지 더하는 상황을 가정했을 때 (I/O 작업이 발생한다고 가정)

: 이를 세개의 쓰레드에서 나눠서 진행한다면, 정해진 시간 동안에 CPU에게 보다 많은 일 시킬 수 있고, Blocked 상태에 놓이는 경우도 나눠서 감당하기 때문에 속도가 높아질 확률이 높다.

: 구현은 쓰레드에 나누어 실행 시킨뒤에 쓰레드의 커널 오브젝트를 관찰하여 종료되었을 때 메인 쓰레드에서 감지하고 종료한다.

case 2 : 쓰레드 종료 시 ExitThread 함수 호출이 유용한 경우(특정 위치에서 쓰레드의 실행을 종료시키고자 하는 경우)

: 언제 어디서나 쓰레드를 종료시킬 수 있지만, 메모리 누수현상이 일어날 수 있다.

: 하지만, 특정 함수가 호출되었을 때 종료시키고 싶다면, 유용하다.

case 3 : 쓰레드 종료 시 TerminateThread 함수 호출이 유용한 경우(외부에서 쓰레드를 종료시키고자 하는 경우)

: 강제 종료이기 때문에 사용하면 좋지 않다.

2. 쓰레드의 성격과 특성

* 힙, 데이터 영역, 그리고 코드 영역의 공유에 대한 검증

: 쓰레드 끼리 공유하고 있는 total 이라는 전역변수에 직접 결과값을 더함으로써 처리해야하는 코드 양이 줄었다,

* 동시접근에 있어서의 문제점

:  하지만 실제로는 total 값을 불러와서 레지스터에 저장한 뒤, 연산하고 다시 total에 저장하려고 할때 블럭킹되어 다른 쓰레드가 실행된 뒤 total에 값을 저장하게 되어, 연산 값이 제대로 반영되지 않는 경우가 다반사 이다.

: 실제로 Context Switching은 빈번하게 나타나며, 메모리 영역을 동시에 참조하는 것은 문제를 일으킬 가능성이 매우 높다.

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

: 쓰레드도 프로세스와 마찬가지로 생성될 때 Usage Count가 2가 된다. 따라서 CloseHandle함수를 곧바로 호출 함으로써 프로세스로부터 쓰레드를 분리할 수 있다.

: 그래야 쓰레드의 종료시점이 쓰레드의 소멸시점이 된다.

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

: 마이크로소프트에서는 멀티 스레드에 안전한 ANSI 표준 라이브러리를 제공하고 있다. 

: CreateThread라는 함수 대신에 _beginthreadex 함수를 사용한다. 독립적인 메모리 블록을 할당한다는 차이점이 있다.

: 라이브러리에서 제공하는 함수를 사용하는 것이 안전하다.

3. 쓰레드의 상태 컨트롤

: 쓰레드의 상태는 운영체제가 관리하나, 필요에 따라서 프로그래머가 변경하는 경우도 발생한다.

* 쓰레드의 상태 변화

: 프로세스와 동일하게 이해하면된다.

: Running, Ready, Blocked

* Suspend & Resume

: SuspendThread는 Blocked 상태에 두는 함수이고 ResumeThread는 Ready 상태에 두기 위한 함수이다.

: SuspendThread 함수가 호출되면 해당 쓰레드의 커널 오브젝트에 SuspendCount(디폴트는 0)이 증가하고 Blocked상태가 된다. 반면 ResumeThread는 SuspendCount를 감소시키는 함수인데, 만약 SuspendThread 함수가 두번 호출되었다면, ResumeThread가 두번 호출되어 SuspendCount를 0으로 만들어야 쓰레드가 Ready 상태가 된다.

: 쓰레드 생성시 SuspendCount를 1로 두어 Blocked상태로 생성할 수 있다.

4. 쓰레드 우선순위 컨트롤    

: 사실 우선순위는 프로세스가 가지는 것이아니라, 쓰레드가 가진다.

: 프로세스는 기준 우선순위를 가지며, 쓰레드가 가지는 상대 우선순위와 결합하여 쓰레드의 우선순위가 결정된다.


뇌를 자극하는 윈도우즈 시스템 프로그래밍
국내도서
저자 : 윤성우
출판 : 한빛미디어 2007.03.30
상세보기



1. 쓰레드란 무엇인가?

* 멀티 프로세스 기반 프로그램

: 서로 다른 프로그램을 실행시키기 위해서 다른 프로세스를 생성하는 것 처럼, 하나의 프로그램이 둘 이상의 일을 동시에 처리하기 위해서 둘이 상의 프로세스를 필요로 한다.

* 멀티 프로세스 운영체제 기반 프로그램의 문제점과 새로운 제안

: 많은 수의 프로세스 생성은 빈번한 컨텍스트 스위칭(Context Switching : 레지스터에 프로세스의 상태 정보를 저장하고 복원하는 일련의 과정)으로 이어져 성능에 영향을 미치기 때문에 부담스러운 작업이다.

: 저장하고 복원하는 컨텍스트 정보의 개수를 줄여준다면, 그 부담을 줄어들 것이다. 컨텍스트 정보란 프로세스 상태정보를 뜻한다.

: A프로세스와 B프로세스가 부모 자식관계라도 일단 생성되고 나면 완전히 별개의 프로세스가 된다.

: 그런데 A프로세스와 B프로세스가 완전히 별개가 아닌 50% 정도만 별개이고 나머지는 공유하는 구조라면 컨텍스트 스위칭의 부담도 반으로 줄지 않을까? 이것이 쓰레드의 탄생한 배경이다.

* 해결책, 쓰레드

: 하나의 프로그램 내에서 둘 이상의 프로그램 흐름을 만들어내기 위해 디자인되었다. 쓰레드간에는 공유하는 상태 정보들이 있기 때문에 이것이 쓰레드의 컨텍스트 스위칭을 빠르게 하는 요인이 된다.

- 쓰레드는 하나의 프로그램 내에서 여러 개의 실행 흐름을 두기 위한 모델이다.

- 쓰레드는 프로세스처럼 완벽히 독립적인 구조가 아니다. 쓰레드들 사이에는 공유하는 요소들이 있다.

- 쓰레드는 공유하는 요소가 잇는 관계로 컨텍스트 스위칭에 걸리는 시간이 프로세스 보다 짧다.

* 메모리 구조 관점에서 본 프로세스와 쓰레드

: 자식 프로세스 생성후 메모리 구조는 서로 아무리 관계가 없게 된다.

: 쓰레드 생성후 메모리 구조는 해당 쓰레드만을 위한 스택을 생성할 뿐 그 이외의 영역은 프로세스영역을 공유하고 있다.

- 쓰레드의 특성 1 : 쓰레드마다 스택을 독립적으로 할당해 준다.

: 스택은 함수 호출 시 전달되는 인자, 되돌아갈 주소값 및 함수내에서 선언하는 변수들을 저장하는 메모리 공간이다.

: 실행흐름의 추가를 위한 최소 조건이다.

- 쓰레드의 특성 2 : 코드 영역을 공유한다.

: 프로세스의 main 함수 이외에도 쓰레드의 main 함수가 따로 존재한다.

- 쓰레드의 특성 3 : 데이터 영역과 힙을 공유한다.

: 쓰레드 간에 힙과 데이터 영역을 공유하기 때문에 IPC가 필요 없어졌다. 전역변수와 malloc 함수를 통해서 동적할당된 메모리 공간은 공유가 가능하다.

: 메모리 영역을 공유할 때 문제가 발생할 수 있기 때문에 프로그래밍시 주의가 필요하다

* Windows에서의 프로세스와 쓰레드

: Windows 입장에서는 프로세스는 단순히 쓰레드를 담는 상자에 지나지 않는다.

: 사실 Windows운영체제에서 프로세스는 상태를(Running, Ready, Blocked)를 지니지 않고 쓰레드가 가진다.

: 스케줄러가 실행단위로 잡는 것도 쓰레드이다.

: 즉, Windows에서 실행의 중심은 프로세스가 아니라 쓰레드이다.

: main Thread라고 부르며 일반적으로 프로그래머에 의해서 직접적으로 생성되는 쓰레드와 구분지어 말한다.

2. 쓰레드 구현 모델에 따른 구분

* 커널레벨(Kernel Level) 쓰레드와 유저레벨(User Level) 쓰레드

-. 커널레벨 쓰레드 모델

: 프로그래머 요청에 따라 쓰레드를 생성 및 스케줄링하는 주체가 커널인 경우, 커널 레벨 쓰레드라고 한다.

: 위의 유저영역은 코드, 데이터, 스팁 및 힙을 가리킨다.

: 커널 영역은 운영체제라는 하나의 소프트웨어를 실행시키기 우해서 필요한 메모리 공간이다.

: 오늘 날의 대부분의 운영체제는 커널 레벨 쓰레드를 기반으로 쓰레드 모델을 지원한다.

-. 유저레벨 쓰레드 모델

: 커널에서 쓰레드 기능을 지원하지 않을 때 생각해 볼 수 있는 모델.

: 커널에 의존적이지 않은 형태로 쓰레드의 기능을 제공하는 라이브러리를 활용할 수 있다.

: 쓰레드를 지원하지 않기 때문에 스케줄링의 대상은 프로세스이다.

: 쓰레드를 스케줄링하는 스케줄러는 유저영역에서 실행된다.

Tip 커널(Kernel) 영역

: 운영체제도 일반적 프로세스과 마찬가지로 함수, 스택, 코드, 전역함수 선언 등등이 동일하게 존재한다.

: 영역이 다른 프로그램일 뿐이고 유저가 사용하는 프로세스와 혼선을 피하기 위해 유저영역, 커널영역으로 나누어 둔것이다.

Tip 컨텍스트 스위칭이 빨라진 쓰레드

- pc : 쓰레드마다 별개의 main함수가 돌아가므로 Context switching이 발생한다.

- fp, sp : 스텍은 별도이므로 Context switching이 발생한다.

- Register : 공유될수는 있으나 디자인에 따라 달라지므로 일반적으로 언급하기 힘들다

- 성능의 차이는 Cash에서 찾자(한번 읽어들인 메인 메모리의 데이터를 저장하고 있다가 CPU가 다시 그 메모리에 저장된 데이터를 요구할 때, 바로 전달해주는 역할)

- 쓰레드는 Cash에 있는 정보를 같이 사용하기 때문에 Cash가 다시 메인 메모리를 읽어들일 필요가 사라진다.


* 커널 모드(Kernel Mode)와 유저모드(User Mode)

: 유저 모드에서는 커널 영역으로의 접근이 금지된다. 

: 커널 모드에서는 모든 영역의 접근이 혀용된다.

: 모드의 전환(커널 모드 유저모드)은 시스템에 부담을 주는 일이다.

: 커널 모드와 유저 모드를 제공하는 것은 프로세서(Processor), 즉 CPU에서 제공하는 일이다.

* 커널 레벨 쓰레드와 유저 레벨 쓰레드의 장점 및 단점

- 커널 레벨 쓰레드

장점 : 안전성, 기능의 다양성

단점 : 커널에서 기능을 제공하기 때문에 성능 저하

- 유저레벨 쓰레드

장점 : 전환 필요없기때문에 성능 좋음

단점 : 프로세스 내에 쓰레드가 하나만 블로킹 되어도 나머지 쓰레드가 작동하기 어려움

: 리눅스의 경우 유저 레벨 쓰레드를 활용하기도 한다.


뇌를 자극하는 윈도우즈 시스템 프로그래밍
국내도서
저자 : 윤성우
출판 : 한빛미디어 2007.03.30
상세보기


+ Recent posts