여러 프로세스나 스레드가 공유된 자원에 동시에 접근하면서 발생할 수 있는 문제입니다. 이러한 상황에서 각 프로세스나 스레드가 자원을 사용하는 순서나 시점이 일정하지 않아서 예상치 못한 결과가 발생할 수 있습니다.
Race Condition으로 인하여 발생하는 문제
상호 배제 (Mutual exclusion)
동일한 자원을 여러 프로세스 또는 스레드가 동시에 접근할 때 발생할 수 있습니다. 이 때, 자원을 한 번에 하나의 프로세스 또는 스레드만이 사용할 수 있도록 제한하는 것이 필요합니다.
기아 상태 (Starvation)
프로세스나 스레드가 자원을 얻지 못해 영원히 대기하는 상태를 말합니다. 이는 일부 프로세스나 스레드가 우선적으로 자원을 사용하는 것을 방지하기 위해 대기 중인 프로세스나 스레드에도 자원을 할당해주어야 합니다.
교착 상태 (Deadlock)
여러 프로세스나 스레드가 서로의 자원을 요청하고 대기하면서, 서로가 끝나기를 기다리는 상황이 발생할 수 있습니다. 이러한 상태는 교착 상태(Deadlock)라고 불리며, 모든 프로세스가 멈추는 문제를 일으킵니다.
세마포어나 뮤텍스 같은 동기화 기법을 사용하여 race condition 문제를 해결할 수 있습니다. 하지만 이러한 기법을 사용하면 성능이 저하될 수 있으므로, 적절한 동기화 전략을 선택해야 합니다.
세마포어(Semaphore)
세마포어는 공유 자원에 대한 접근을 제어하기 위한 동기화 기법 중 하나입니다. 세마포어는 정수형 변수와 두 개의 원자적인 연산(P, V)으로 구성됩니다.
- P(S): 세마포어 값을 1 감소시키고, 세마포어 값이 음수가 되면 호출한 스레드를 대기 상태로 전환합니다.
- V(S): 세마포어 값을 1 증가시키고, 세마포어 값이 0보다 작거나 같은 경우 대기 중인 스레드 중 하나를 깨웁니다.
세마포어를 사용하는 예제 코드를 살펴보겠습니다. 아래 코드는 세마포어를 사용하여 크리티컬 섹션에 대한 접근을 제어하는 코드입니다.
import java.util.concurrent.Semaphore;
class SharedResource {
private Semaphore semaphore = new Semaphore(1);
private int sharedValue;
public void increment() throws InterruptedException {
semaphore.acquire();
sharedValue++;
System.out.println(Thread.currentThread().getName() + " incremented the shared value. Current value: " + sharedValue);
semaphore.release();
}
}
class MyThread extends Thread {
private SharedResource sharedResource;
public MyThread(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
public void run() {
try {
sharedResource.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class SemaphoreExample {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
for (int i = 0; i < 5; i++) {
new MyThread(sharedResource).start();
}
}
}
위 코드에서 SharedResource 클래스는 크리티컬 섹션에 대한 접근을 제어하는 세마포어를 가지고 있습니다. increment() 메서드에서는 세마포어를 acquire() 하고, 크리티컬 섹션에 대한 작업을 수행한 후에 release() 합니다. MyThread 클래스는 SharedResource 객체를 생성자로 받아서 increment() 메서드를 호출합니다. 여러 스레드에서 SharedResource 객체를 공유하더라도 크리티컬 섹션에는 한 번에 하나의 스레드만 접근할 수 있습니다.
뮤텍스(Mutex)
상호 배제를 위한 동기화 기법 중 하나로, 공유 자원에 대한 접근을 동기화하여 한 순간에 오직 한 스레드만이 자원에 접근하도록 합니다. 뮤텍스는 P (Proberen, 얻기)와 V (Verhogen, 높이기) 연산을 사용하여 구현합니다. P 연산은 뮤텍스에 락을 걸어 자원에 대한 접근을 요청하고, V 연산은 락을 해제하여 다른 스레드가 자원에 접근할 수 있도록 합니다.
다음은 자바에서 뮤텍스를 구현하는 예제입니다.
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 t1 = new Thread(() -> {
mutex.lock();
try {
// Critical Section
} finally {
mutex.unlock();
}
});
Thread t2 = new Thread(() -> {
mutex.lock();
try {
// Critical Section
} finally {
mutex.unlock();
}
});
t1.start();
t2.start();
}
}
위 예제에서는 ReentrantLock 클래스를 사용하여 뮤텍스를 구현하였습니다. lock() 메서드로 뮤텍스에 락을 걸고, unlock() 메서드로 락을 해제합니다. try-finally 구문을 사용하여 뮤텍스를 해제하는 것이 안전합니다.
'CS' 카테고리의 다른 글
페이지 교체 알고리즘 (0) | 2023.04.08 |
---|---|
페이징 & 세그멘테이션 (0) | 2023.04.08 |
데드락(Deadlock, 교착상태) (0) | 2023.04.08 |
CPU 스케줄링 (0) | 2023.04.08 |
IPC(Inter-Process Communication) (0) | 2023.04.08 |