임계 영역 (Critical Section)
스터디 Stacked-Book에서 실습과 그림으로 배우는 리눅스 구조 책을 참고하여 학습 후 정리한 글입니다.
임계 영역 (Critical Section)
- 다중 스레드 또는 다중 프로세스 환경에서 공유 자원에 접근하는 코드 영역
- 이 영역에서 여러 프로세스 또는 스레드가 동시에 접근하면 데이터 일관성 문제가 발생할 수 있으므로, 이를 효과적으로 관리하기 위한 메커니즘이 필요하다
임계 영역을 관리하기 위한 개념
- Entry Section, Critical Section, Exit Section은 상호배제(Mutual Exclusion) 문제를 해결하기 위한 임계 영역(critical section)의 구성요소 중 일부이다.
- entry-section : 프로세스가 critical-section에 접근하기 전에 접근 허용 여부를 확인하는 영역
- critical-section : 실제 작업이 수행되는 영역
- exit-section : critical-section에서 작업이 끝난 후 프로세스가 나왔다는 것을 다른 프로세스에 알려주는 영역
임계 영역 문제를 해결하기 위해 필요한 세가지 조건
1. 상호 배타 (Mutual Exclusion)
- 한 프로세스가 임계 영역에 들어갔을 때 다른 프로세스는 임계 영역에서 실행될 수 없다.
- 임계 영역에 들어가려고 시도하는 프로세스는 임계영역에 진입한 프로세스가 나올 때까지 보류되다가 권한을 부여받을 때, 임계영역에 들어가게 된다.
2. 진행 (Progress) _ deadlock을 피하기 위한
- 임계 영역에 아무도 없는 상태에서 임계 영역에 들어가고자 하는 프로세스는 들어갈 수 있어야 한다.
3. 한정된 대기 (Bounded Waiting) _ starvation을 피하기 위한
- 프로세스가 임계 영역에 접근하려고 시도한 후에는 유한한 시간 내에 접근을 허용해야 하며, 무한정 대기하지 않아야 한다.
임계 영역을 해결하기 위한 방법
1. 뮤텍스
- 뮤텍스는 임계 영역에 들어가기 전에 뮤텍스 객체를 획득해야 하며, 다른 프로세스나 스레드가 해당 뮤텍스를 획득하려고 시도하면 대기 상태에 들어간다.
- 사용 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MutexExample { private static Lock mutex = new ReentrantLock(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { mutex.lock(); try { // 임계 영역에 접근하는 코드 } finally { mutex.unlock(); } }); Thread thread2 = new Thread(() -> { mutex.lock(); try { // 다른 스레드도 임계 영역에 접근하지 못함 } finally { mutex.unlock(); } }); thread1.start(); thread2.start(); } }
2. 세마포어
- 정수 값을 사용하여 프로세스가 리소스에 대한 접근을 조절하는 데 사용된다.
P (wait)
와V (signal)
연산을 통해 리소스의 사용과 해제를 조절하여 다중 프로세스 간의 상호 배제와 진행 조건을 만족시킬 수 있다.- P (wait) 연산은 리소스를 기다리는 동작을 나타내며, 리소스를 얻을 때까지 기다린다.
- V (signal) 연산은 리소스를 해제하는 동작을 나타내며, 다른 프로세스에게 리소스를 넘겨준다.
- 사용 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
import java.util.concurrent.Semaphore; public class SemaphoreExample { private static Semaphore semaphore = new Semaphore(1); public static void main(String[] args) { Thread thread1 = new Thread(() -> { try { semaphore.acquire(); // 임계 영역에 접근하는 코드 } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }); Thread thread2 = new Thread(() -> { try { semaphore.acquire(); // 다른 스레드도 임계 영역에 접근하지 못함 } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }); thread1.start(); thread2.start(); } }
3. 모니터
- 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고 해당 접근에 대해 인터페이스만 제공하는 방법이다.
- 모니터 큐를 통해 공유 자원에 대한 작업들을 순차적으로 처리한다.
- 사용 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
public class MonitorExample { private static Object monitor = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (monitor) { // 임계 영역에 접근하는 코드 } }); Thread thread2 = new Thread(() -> { synchronized (monitor) { // 다른 스레드도 임계 영역에 접근하지 못함 } }); thread1.start(); thread2.start(); } }
스터디에서 한 분이 잘 설명된 글을 공유해 주셨다 참고하면서 공부하니 이해가 더 쉬웠다.
This post is licensed under CC BY 4.0 by the author.