상호 배제(Mutual Exclusion)를 위한 OS에서 제공하는 동기화 기법으로는 Spin Lock, Mutex, Semaphore가 있다
단순히 Race Condition을 피하기 위한 상호 배제만 달성할 뿐이지, 데드락(Deadlock)이나 기아(Starvation)을 발생시킬 수도 있다
Spin Lock
- lock을 얻고자 하는 쓰레드가 CPU점유를 다른 쓰레드에 내주지 않고, 계속해서 lock을 얻을 수 있는지 무의미한 루프를 돌면서(busy wait) 체크하는 방법이다.
다른 쓰레드에게 CPU점유를 내주지 않으니 Context Switching을 함으로써 생기는 오버헤드가 없다는 장점이 있지만, lock을 얻기전까지 쓸데 없는 루프를 돌면서 CPU를 점유하는데, 그 점유를 다른 쓰레드에게 내주지도 않으니 lock을 얻기까지 오래걸린다면 CPU 점유율만 높힐 수 있다는 단점도 있다.
여기서 궁금한 점이 있는데 Single Core일 때는 Spin Lock을 쓰는게 과연 가능할까? 였다. 검색을 해보니 "Single Core일때 Spin Lock은 성능상 이득이 없다"라고 한다. 그런데 나는 성능상 이득이 아니라,
Q) "애초에 Context Switching이 안되는 쓰레드에 Single Core니까, 결국 busy wait를 하면서 lock을 얻을 수 있는지만 체크하고 다른 코드는 일체 실행하지 않을 것이다. 그러면 무한 루프에 걸린것 처럼 프로그램을 실행해도 데드락에 걸린것 처럼 아무것도 실행하지 않을것 같다" 이런 생각?을 했다
A) 이런 엉뚱한 생각은 나밖에 안하는 것 같은데, Stack Overflow나 Quora를 참조해보면 효율성 차원의 문제가 아니라 애초에 말이 안되거나 컴파일이 안된다고까지 하는 것 같다. 일정한 Time Slice를 두고 해당 Time Slice를 넘어서 점유하면 강제로 점유를 빼앗거나 하는 방식이면 가능할텐데 그러면 Context Switching을 해버리기 때문에 Spin Lock이 아니다.
https://stackoverflow.com/questions/42286440/why-do-spin-locks-make-no-sense-on-a-single-cpu
https://www.quora.com/Multiprocessing-Why-arent-spinlocks-appropriate-for-a-single-processor
Mutex
- 하나의 쓰레드나 프로세스가 동기화가 필요한 데이터에 접근하거나 Critical Section에 진입하려 할 때 쓰는 동기화 기법. 중요한 것은 동기화 대상이 하나의 쓰레드라는 것이다. 하나의 쓰레드가 lock을 획득해서 진입하면, 진입하고자 하는 다른 쓰레드는 이미 진입한 쓰레드가 lock을 해제할 때 까지 Sleep 상태로 기다렸다가 WakeUp되면 다시 lock을 획득할 수 있는지 확인한다. Spin Lock과의 큰 차이는 lock 획득 가능 여부 확인을 CPU 점유한 채로 확인하는게 아니라 Sleep상태로 대기 했다가, WakeUp 했을때만 확인한다는 것이다.
Mutex는 Lock을 Acquire했다면 필요한 자원을 '소유'했다는 개념이 있어서 쓰레드가 필요한 작업이 끝나면 반드시 본인이 Release까지 해줘야 하는 책임이 있다.
이해가 잘 되는 예시를 들어주신 다른 블로거의 글이 있었다
https://heeonii.tistory.com/14
Semaphore
- 여러개의 쓰레드나 프로세스가 동기화가 필요한 데이터에 접근하거나 Critical Section에 진입하려 할 때 쓰는 동기화 기법. Mutex와 다른점은 동기화 대상이 여러개가 될 수 있다는 것이다. Semaphore는 간단히 지금 사용 가능한 자원의 수를 나타내는 공유되는 값이라고 보면 될 것 같다.
그런데 Semaphore를 Mutex와 단순히 여러개의 쓰레드를 동기화를 위한 기법일 뿐만 아니라 추가적으로 다른 점이 있다. Semaphore는 마치 싱글톤 관리자처럼, 하나의 쓰레드가 소유할 수 있는 개념이 아니다. 지금 사용 가능한 자원의 수를 알려주는 변수일 뿐이며, 지금 사용 가능한 자원이 없어서 기다리고 있는 쓰레드가 있었다면, 사용 가능한 자원이 생기면 쓰레드에게 알려주는 기능을 하기도 한다.(Sema라는 뜻 자체가 Signal이라는 의미이고, -Phore는 Carrier 라는 의미를 갖고 있다고 한다)
http://www.rtos.be/2013/05/mutexes-and-semaphores-two-concepts-for-two-different-use-cases/).
Semaphore의 종류
1. Binary Semaphore : Count가 0 또는 1인 Semaphore
2. Counting Semaphore : Count가 1 이상이 될 수 있는 Semaphore
특징
1. Binary Semaphore는 곧 Mutex가 된다. 즉, Semaphore는 Mutex가 될 수 있지만, Mutex는 Semaphore가 될 수 없다.
2. Mutex는 '소유' 개념이 있어서 Lock을 Acquire한 주체가 Release까지 해야할 책임이 있다. 하지만 Semaphore는 Lock을 얻은 주체가 아닌 다른 쓰레드가 Release할 수도 있다.
* 위에서 언급했듯이, 소개한 기법들이 데이터 동시 접근을 막는 방법일 뿐이지 데드락(Deadlock)이나 특정 쓰레드가 CPU점유를 전혀 할 수 없는 기아(Starvation) 현상까지 완벽히 예방해주진 않는다. 예를 들어 아래와 같은 경우 Semaphore를 사용했을 때 데드락이 걸릴 수 있다
# Thread 1 acquire A
# Thread 2 acquire B
# Thread 1 try to acquire B, waiting on #2 to release B forever
# Thread 2 try to acquire A , waiting on #1 to release A forever
또한 대기중인 쓰레드를 Wakeup하는 순서는 비결정적이므로, 운이 안좋은 쓰레드는 끝까지 CPU를 점유할 수 없을 수도 있어서 기아 현상이 생길 수도 있다.
그럼 어떤 조건에서 Deadlock이 발생하고, 회피하거나 예방하는지는 다음 포스팅에서 작성하겠다
'공부 > 운영체제' 카테고리의 다른 글
시스템 콜(System Call) (0) | 2022.09.06 |
---|---|
데드락(Deadlock) (0) | 2022.09.02 |
쓰레드(Thread), 프로세스(Process)란? (2) | 2022.09.01 |
Windows에서의 동기화 기법 (0) | 2022.01.22 |