스레드란?

프로세스는 독립적인 메모리 공간을 이용함으로 Context Switching이 발생하고 서로 통신을 위해 IPC가 필요하다. 이러한 문제점을 해결하고자 Thread가 등장했다. 스레드는 CPU의 기본단위로 하나의 프로그램 내에 여러 개의 실행흐름을 위한 모델이다. 스레드는 code 영역을 공유하기 때문에 프로세스가 가지는 함수를 모두 호출 할 수 있고, data와 heap영역 역시 공유하기에 스레드 간의 통신이 가능하다.Stack과 register는 독릭접으로 할당되어 독립적인 함수 호출이 가능하며 이는 독립적인 실행 흐름이 가능하다는 것을 의미한다.

대표적인 예가 웹서버가 있다. 웹 서버는 클라이언트의 요청을 listen하는 별도의 스레드를 생성한다. 요청이 들어오면, 새로운 스레드를 생성해 클라이언트의 요청을 처리하고 listen 스레드는 계속해서 요청을 기다리고 있다.

병행 프로그래밍은 프로세스 사이를 빠르게 오가며 다수의 프로세스를 진행 시키는 것, 병렬 프로그래밍은 하나 이상의 테스크를 동시에 수행하는 것을 말한다. 즉 병행 프로그래밍에서는 T1, T2, T1, T2 와 같이 동작하고 코어가 여러개인 병렬 프로그래밍에서는 (T1, T2), (T1,T2) 와 같이 동작한다.

Windows나 Ptheads에서는 공유 데이터가 단순히 전역 변수로 선언되어 공유가 쉽다. 그러나 Java는 순수 객체 지향언어로 이러한 전역변수의 개념을 제공하지 않는다. 따라서 Java에서 둘 이상의 스레드가 데이터를 공유해야 한다면 공유 객체에 대한 참조를 적당한 스레드에게 전달함으로서 공유한다.

스레드 풀

서비스를 할 때마다 스레드를 생성하고 해당 서비스가 끝날 때마다 다시 스레드를 삭제한다면 비효율적이다. 또한 최대 스레드 수가 몇 개까지 가능 할 수 있을지 한계를 정해야한다. 이러한 문제점에서 출발한 개념이 스레드 풀이다. 기본 아이디어는 프로세스를 시작 할 때 일정한 수의 스레드를 미리 풀로 만들어 두는 것이다. 요청이 들어오면 이 풀에서 하나씩 꺼내주고, 요청이 끝난 스레드는 다시 풀로 들어가 다음 작업을 기다린다.

fork 및 exec

fork()를 호출하면 새로운 프로세스는 모든 스레드를 복제해야 하는가? exec()호출은 매개변수로 지정된 프로그램이 모든 스레드를 포함한 전체 프로세스를 교체한다. 이 경우에는 이전의 모든 스레드를 복제 할 필요가 없다. 그러나 새 프로세스가 fork() 후 exec를 하지 않는다면 모든 스레드를 복제해야한다.

멀티스레드 환경에서 fork를 사용할 경우 부모프로세스의 메모리를 전부 복사하고, fork를 호출한 스레드를 제외한 나머지 스레드들을 모두 죽여버린다. 때문에 fork를 호출한 스레드 이외의 스레드는 점유하고 있던 자원을 해제하지 못해 메모리릭이 발생하고, 해당 스레드가 mutex나 조건변수들을 가지고 있는 상태에서 죽어버린다면 criticalsection에는 다시 진입할 수 없게되어 주의해야한다.