스레드(Thread)는 CPU 수행의 기본 단위 또는 프로세스 안의 제어권의 흐름이다.
스레드가 수행되는 환경을 Task라고 부르는데, 전통적인 프로세스는 하나의 스레드가 있는 Task와 일치한다.
한 프로세스가 하나의 스레드를 이용하여 한 번에 한 작업만 수행하는 것은 싱글 스레드(Single thread), 한 프로세스가 여러 스레드로 동시에 여러 작업을 수행하는 것은 멀티 스레드(Multi thread)라고 한다.
멀티 스레드는 코드와 adress space, operating resource 를 공유한다.
프로세스 내의 스레드는 모두 각각 독립적인 실행 파일이며, 모든 스레드는 프로세스의 일부이다. 프로세스를 여러 개 수행해도 되지만 굳이 스레드를 사용하는 이유는 다음과 같다.
프로세스를 생성하거나 Context switching 하는 작업은 너무 무겁고 잦으면 성능 저하가 발생하는데, 스레드를 생성하거나 switching 하는 것은 그에 비해 가볍다.
두 프로세스가 하나의 데이터를 공유하려면 메시지 패싱이나 공유 메모리 또는 파이프를 사용해야 하는데, 이는 효율도 떨어지고 개발자가 구현, 관리하기도 번거롭다.
->어떻게 다른지
응답성(Responsiveness)
싱글 스레드인 경우, 작업이 끝나기 전까지 사용자에게 응답하지 않는다. 반면 멀티스레드인 경우 작업을 분리해서 수행하므로 실시간으로 사용자에게 응답할 수 있다.
자원 공유(Resource sharing)
프로세스는 오직 공유 메모리나 메시지 패싱을 이용해서 자원을 공유할 수 있지만, 스레드는 자신이 속한 프로세스 내의 스레드들과 메모리나 자원을 공유하여 효율적으로 사용할 수 있다.
경제성(Economy)
프로세스를 새로 생성하는 비용보다 스레드를 새로 생성하는 게 훨씬 싸다. 그리고 Context switching의 오버헤드 또한 스레드가 더 경제적이다. 실제로 Solaris에서 프로세스 생성은 스레드 생성보다 30배 느리고, switching은 5배 느리다.
확장성(Scalability)
싱글 스레드인 경우 한 프로세스는 오직 한 프로세서에서만 수행 가능하다. 반면 멀티 스레드인 경우 한 프로세스를 여러 프로세서에서 수행할 수 있으므로 훨씬 효율적이다.
(Achieve concurrency and parallelism, Throughput (overlap computation and I/O), Utilization of MP Architectures)
유저 스레드(User-level Thread)는 커널 위에서 커널의 지원 없이 유저 수준의 스레드 라이브러리(Thread Library)가 관리하는 스레드다.
반면 커널 스레드(Kernel-level Thread)는 커널이 지원하는 스레드다.
이 커널을 multithreaded kernel 이라고 한다.
- Most contemporary operating systems (e.g., Windows XP, Linux, Solaris, Mac OS X, etc.) are multithreaded kernels.
커널 스레드를 사용하면 안정적이지만 유저 모드에서 커널 모드로 계속 바꿔줘야 하기 때문에 성능이 저하된다. 반대로 유저 스레드를 사용하면 안정성은 떨어지지만 성능이 저하되지는 않는다
user-level thread와 kernel-level thread 사이의 mapping
Many-to-One Model
하나의 커널 스레드에 여러 유저 스레드를 연결하는 모델이다. 유저 공간의 스레드 라이브러리를 통해서 스레드가 관리되므로 효율적이다. 라이브러리를 위한 모든 코드나 자료구조가 유저 공간에 존재하므로 라이브러리의 함수 호출이 시스템 콜이 아니라 지역 함수 호출의 결과를 낳기 때문이다.
반면, 한번에 한 유저 스레드만 커널에 접근할 수 있기 때문에 멀티 프로세서 시스템에서 병렬적인 수행을 할 수 없어 요즘에는 잘 사용되지 않는 방식이다. 한 유저 스레드의 시스템 콜로 인해 block 되면 프로세스 전체가 block 되기 때문이다.
One-to-One Model
하나의 커널 스레드에 하나의 유저 스레드가 대응하는 모델이다. 동시성(Concurrency)을 높여주고, 멀티 프로세서 시스템에서 동시에 여러 스레드를 수행할 수 있도록 해준다.
단점으로는, 유저 스레드를 늘리면 커널 스레드도 똑같이 늘어나는데, 커널 스레드의 생성은 오버헤드가 크기 때문에 성능 저하가 발생할 수 있다.
Many-to-Many Model
여러 유저 스레드에 더 적거나 같은 수의 커널 스레드가 대응하는 모델이다. 운영체제는 충분한 수의 커널 스레드를 만들 수 있으며, 커널 스레드의 구체적인 개수는 프로그램이나 작동 기기에 따라 다르다. 멀티 프로세서 시스템에서는 싱글 프로세서 시스템보다 더 많은 커널 스레드가 만들어진다.
완전한 동시성은 아니지만, Many-to-one Model에 비해 더 높은 동시성을 갖는다. 그리고 One-to-One Model의 단점이었던 커널 스레드 생성의 오버헤드도 걱정할 필요 없다.
Two-level Model
Many-to-Many Model에서 확장된 개념이다. 특정 유저 스레드를 위한 커널 스레드를 별도로 제공하는 모델이다. 점유율이 높아야 하는 유저 스레드를 더 빠르게 처리할 수 있다.
Multi-threaded 프로그램을 디자인할 때 고려해야 할 몇 가지
만약 fork( ) 이후에 exec( )을 바로 호출한다면 exec( )으로 인해 스레드를 포함한 전체 프로세스가 대체되기 때문에 모든 스레드를 복제하는 것은 불필요할 것이다. 그렇지 않으면 모든 프로세스를 복제해야 한다.
따라서 몇몇 UNIX 시스템은 두 버전의 fork( )를 가진다. -> 무엇이 더 효율적인지 모르기 때문에
시그널(Signal)은 특정한 사건이 발생했다고 프로세스에게 알려주기 위해 UNIX 시스템에서 사용하는 것이다.
자원이나 시그널의 원인에 따라 두 종류로 나뉜다.
1) Synchronous signals
2) Asynchronous signals
시그널을 다루는 방법(Signal Handling) 또한 다양하다.
싱글 스레드 프로그램에서는 시그널이 특정 사건에 의해 생성되고, 프로세스에 전달된 후 다뤄진다.
멀티 스레드 프로그램에서는
시그널을 제공한 스레드로 시그널이 전달되거나(ex. Synchronous signals),
프로세스 내의 모든 스레드에 전달되거나(ex. process termination signal),
프로세스 내의 특정한 스레드에 전달될 수 있다(some asynchronous signals to non-blocking threads).
혹은 프로세스의 모든 시그널을 전달받는 특별한 스레드를 할당하는 방법도 있다.
Thread Cancellation
스레드가 끝나기 전에 종료시키는 두 방식이 있다.
1) Asynchronous cancellation : 목표 스레드(Target thread)를 즉시 종료시킨다.
2) Deferred cancellation : 목표 스레드가 종료되어야 하는지 주기적으로 체크한다.
Thread Pools
스레드를 요청할 때마다 매번 새로운 스레드를 생성, 수행, 삭제를 반복하면 성능이 저하된다. 따라서 미리 스레드 풀(Thread pools)에 여러 스레드를 만들어두고 요청이 오면 스레드 풀에 기존에 존재하던 스레드를 할당해주는 방법을 사용한다.