스레드 이슈(Threading Issues)

By 01/10/2018OS Concept

스레드 이슈(Threading Issues)

 

다중 스레드 프로그램에서 발생하는 이슈에 대해서 알아 보자.

 

[Fork() 및 Exec() 시스템 호출]

다중 스레드 프로그램에서는 fork()와 exec() 시스템 호출의 의미가 달라질 수 있다. 한 프로그램의 스레드가 fork()를 호출하면 새로운 프로세스는 모든 스레드를 복사 또는 fork()시스템을 호출한 스레드만 복제 한다.

 

Exec() 시스템을 호출하면 exec()의 매개변수로 지정된 프로그램이 모든 스레드를 포함한 전체 프로세스를 대체 시킨다. fork()를 부르자마자 다시 exec()을 부른다면 exec()에서 지정한 프로그램이 곧 모든 것을 다시 대체할 것이기 때문에 모든 스레드를 복제해서 만들어주는 것은 불필요하다. Fork() 후 exec()를 하지 않는다면 새 프로세스는 모든 스레드들을 복제 해야 한다.

 

[취소(cancellation)]

스레드 취소는 스레드가 끝나기 전에 그것을 강제 종료시키는 작업이다. 취소 되어야 할 스레드를 목적 스레드라고 하며 두 가지 방식으로 발생 할 수 있다.

  • 비동기식 취소(asynchronous cancelation) : 한 스레드가 즉시 목적 스레드를 강제 종료 한다
  • 지연 취소(Deferred cancellation) : 목적 스레드가 주기적으로 자신이 강제 종료되어야 할지를 확인 한다.

 

스레드 취소를 어렵게 만드는 것은 취소 스레드의 할당된 자원 문제(스레드의 자원 공유 및 자료 구조 갱신 중 취소) 때문이다.

  • 비동기 취소는 운영체제가 취소된 스레드로부터 자원을 회수 화지 못하여 사용 가능 상태로 못 만들 수 있다.
  • 지연 취소에서 목적 스레드는 취소 예정이라고 표시하지만 실제 취소는 목적 스레드가 취소 여부를 결정하기 위해 플래그를 검사한 이후에 일어 난다. 스레드들은 자신이 취소되어도 안전하다고 판단되는 시점에서 취소 여부를 검사할 수 있으며 Pthread는 이 지점을 취소점(cancellation point)라 한다.

 

[신호 처리(Signal Handing)]

신호는 알려줄 사건의 근원지나 이유에 따라 동기식 또는 비동기식으로 전달 될 수 있으며 다음과 같은 형태로 전달 된다.

  1. 신호는 특정 사건이 일어나야 생성 된다.
  2. 신호가 생성되면 프로세스에게 전달 된다.
  3. 신호가 전달되면 반드시 처리 되어야 한다.

 

동기식 신호의 예는 불법적인 메모리 접근, 0으로 나누기 등이 있으며 동기식 신호는 신호를 발생시킨 연산을 수행한 동일한 프로세스에게 전달 된다.

 

비동기식 신호의 예는 외부로부터 발생되는 프로세스 신호이며 <control><c>같은 특수한 키를 눌러 프로세스를 강제 종료 또는 타이머가 만료되는 경우를 포함한다. 비동기식 신호는 통상 다른 프로세스에게 전달 된다.

 

디폴트 신호 처리기는 커널에 의해 실행 된다. 이 신호를 처리하기 위하여 호출되는 사용자 정의신호 처리기에 의해 다른 방식으로 처리 될 수 있다. 신호에 따라 무시 또는 강제 종료 된다. 다중 스레드 프로그램에서의 처리는 다음과 같다.

  1. 신호가 적용될 스레드들에게 전달 한다.
  2. 모든 스레드들에게 전달한다
  3. 일부 스레드에게만 선택적 전달 한다.
  4. 특정 스레드가 모든 신호를 전달받도록 지정한다

 

동기식 신호는 그 신호를 야기한 스레드들에게 전달되어야 하고 다른 스레드들에게는 전달 되면 안된다. 비동기 스레드의 신호는 명확하지가 않아 프로세스 내 모든 스레드들에게 전달 되어야 한다.

 

Windows는 신호를 명시적으로 지원하지 않지만 비동기식 프로시저 호출(Asynchronous Procedure Call, APC)를 사용해 대리 실행(emulate)할 수 있다. APC는 사용자 스레드들이 특정 사건의 발생을 전달받았을 때 호출될 함수를 지정할 수 있다.

 

 

[스레드 풀(thread pool)]

요청마다 새로운 스레드를 생성하는 것은 새로운 프로세스를 생성하는 것보다는 확실히 오버헤드가 적지만 다음과 같은 문제점을 가지고 있다.

  • 스레드를 생성하는데 소요되는 시간(사용 후 폐기될 용도라면 더 크게 느껴진다)
  • 요청마다 새로운 스레드를 생성하여 제공한다면 시스템에서 동시에 실행 할 수 있는 스레드의 한계를 정해야 한다. 무한정 생성시 언젠가는 자원이 고갈되기 때문이다.

 

위의 두 가지 사례를 해결하기 위하여 스레드 풀을 이용한다. 스레드 풀은 프로세스를 시작 할 때 미리 일정한 수의 스레드들을 풀로 만들어 두는 것이다. 이 스레드들은 평소에 하는 일 없이 대기 하다가 요청이 들어오면 풀에서 할당한다. 요청 작업이 완료 되면 스레드는 다시 풀로 돌아가 다음 작업을 대기 한다. 풀에 남아 있는 스레드가 모두 소진되면 서버는 자유 스레드가 생길 때까지 기다린다.

 

스레드 풀의 스레드 개수는 CPU 수, 메모리 용량, 동시 요청 클라이언트 최대 개수 등을 고려하여 정한다. 스레드 풀 활용도를 동적으로 풀의 크기를 바꾸어 줄 수 도 있다. 이러한 구조는 부하가 작은 시스템에서 더 작은 풀을 유지하므로 적은 메모리를 사용한다.

 

 

[스레드 데이터(Thread Specific Data)]

스레드의 장점으로 한 프로세스에 속한 스레드들은 그 프로세스의 자료를 모두 공유한다. 그런 각 상황에 따라서 스레드들은 자기만 접근할 수 있는 자료를 가질 필요가 있다. 이러한 자료를 스레드별 데이터라고 한다. Win32, Pthread, JAVA를 포함한 대부분의 스레드 라이브러리들은 스레드별 데이터를 지원한다.

 

[스케줄러 액티베이션(Scheduler Activation)]

다중 스레드는 라이브러리와의 통신문제를 고려해야 한다. 다대다 또는 두 수준 모델을 구현하는 많은 시스템들은 사용자와 커널 스레드 사이에 중간 자료 구조를 둔다. 이 자료구조는 통상 경량 프로세스 또는 LWP라 부른다.

 

사용자 스레드 라이브러리와 커널 스레드간의 통신방법 중의 하나는 스케줄러 액티베이션(scheduler activation)이라고 알려진 방법이다.

  1. 커널은 가상 처리기(LWP) 집합을 제공하고 응용프로그램은 사용자 스레드를 가용한 가상 처리기로 스케줄 한다.
  2. 커널은 응용 프로그램에게 특정 사건에 대해 알려 주어야 한다. 이를 Upcall 이라 한다.
  3. Upcall은 스레드 라이브러리의 upcall 처리기에 의해 처리되고 upcall 처리기는 가상 처리기 상에서 실행 되어야 한다.
  4. 커널은 새로운 가상 처리기를 응용 프로그램에게 할당한다.
  5. 응용프로그램은 새로운 가상 처리기 상에서 upcall 처리기를 수행하고 이 upcall 처리기는 봉쇄 스레드의 상태를 저장하고 스레드가 실행 중이던 가상 처리기를 반환한다.
  6. Upcall 처리기는 새로운 가상 처리기에서 실행 가능한 다른 스레드를 스케줄러 한다.
  7. 봉쇄 스레드가 기다리던 사건이 발생하면 커널은 이전에 봉쇄되었던 스레드가 이제 실행 가능 하다는 사실을 알려주는 또 다른 upcall을 스레드 라이브러리에 전달한다.
  8. 커널은 새로운 가상 처리기를 할당하거나 사용자 스레드 하나를 할당하여 처리기에서 upcall 처리기를 실행 한다.
  9. 봉쇄가 풀린 스레드를 실행 가능 상태로 표시한 후 응용프로그램은 가용한 가상 처리기 상에서 다른 실행 가능한 스레드를 실행 한다.

 

 

[참고자료]

Operating System Concept / 홍릉과학출판사

Thread Pool Pattern : http://en.wikipedia.org/wiki/Thread_pool_pattern

 

 

operation system, 운영체제, 운영체제구조, 컴퓨터구조, 스레드, Pthread, Win32thread, javathread, OSConcept, 스레드라이브러리, sqlmvp, SQL Angeles

 

 

Leave a Reply

%d bloggers like this: