
우리가 이전 포스트에서 동기화 메커니즘에 어떤 종류가 있는지 알아봤다. 이번 포스트에서는 실질적으로 동기화 메커니즘이 왜 필요한 지에 대해서 알아보겠다.
동기화 메커니즘은 자원 동기화가 목적이다. 그런데, 이 자원 동기화 없이 비동기 프로그래밍을 한다면 어떤 상황이 발생할까? 한번 살펴보자.
우선 현실 문제로 예시를 들어보겠다.
Bob과 Alice가 동일한 은행 계좌에서 동시에 금액을 출금하려고 한다. Bob은 ATM에서, Alice는 온라인 뱅킹을 통해 거래를 한다. 동기화 메커니즘이 없다면, 두 거래가 거의 동시에 이루어져 계좌의 잔액을 정확히 감소시키지 못할 수 있다. 이유는 잔액이 과다하게 표시되어 두 사람 모두 출금을 시도할 수 있게 되고, 결과적으로 계좌는 마이너스 잔액을 보게 된다.
Bob과 Alice가 공동 작업을 하는 문서를 동시에 편집한다고 가정하자. Bob은 문서의 첫 번째 부분을 수정하고, Alice는 거의 동시에 마지막 부분을 수정한다. 여기서 동기화 없이는 Bob의 변경사항이 Alice의 변경사항에 의해 덮어질 수 있다.
위 예시들이 모두 동기화 메커니즘을 적용하지 않아서 나오는 문제들이다. 흔히 데이터 레이스, 레이스 컨디션이라고 불린다.
위 예시들이 모두 이 데이터 레이스다. 데이터 레이스란 변경 가능한 메모리에 접근하는 동안 쓰기 작업을 하는 것이다.
즉, 순서가 알 수 없기에 실행 결과를 알 수 없어져 예상할 수 없는 결과가 나온다는 아주 위험한 오류다. 위 예시들만 봐도 그렇다.
레이스 컨디션은 또 다른 개념인데, 데이터 레이스의 상위 개념이라고 착각할 수 있지만, 아니다. 공통점은 존재하되 서로 다른 개념이라고 볼 수 있다.
레이스 컨디션이란 타이밍이나 순서가 코드 조각의 정확성에 영향을 미칠 때 발생하는 여러 오류들을 일컫는 말이다.
데이터 레이스는 읽기/쓰기가 명확하게 나와있지만 이건 포괄적인 개념이다. 이런 점에서 데이터 레이스와 차이가 있다. 단순히 읽기만 하는데 순서를 알 수 없다면? 이건 데이터 레이스가 아닌 것이다.
하지만 동기화 메커니즘을 적용한다고 해도 관리를 잘못하면 생길 수 있는 문제들이 있다.
바로 데드락이란 개념이다.
데드락은 두개 이상의 락(Lock)이 서로의 작업이 끝나기를 기다리며 작업 진행이 안되는 상태에 빠지는 것을 말한다.
데드락은 다음과 같은 상황을 모두 만족시켜야 일어날 수 있다.
모두 전 포스팅을 읽고 왔다면 굉장히 이해하기 쉬울 것이다.
Mutex나 Semaphore, SpinLock 등등 동기화 메커니즘들을 잘못 사용할 때 데드락이 발생할 수 있다.
이런 상황이 발생되면 더 이상 작업이 진행되지 않게 된다.
이런 상황을 예방하는 방법은 위 필요 조건을 깨면 된다. 예를 들어 러스트에서는 Mutex 말고 RwLock이라는 읽을 때 상호 배제가 없어도 되는 락이 존재한다. 이런식으로 예방할 수도 있고,
데드락을 회피할 수 있는 방법도 있다. 은행원 알고리즘이라고 불리는 이 회피 방법은 각 프로세스가 최대로 요구할 수 있는 자원 양을 사전에 알고, 이를 기반으로 안전한 자원 할당 결정을 합니다. 현재의 자원 요청이 시스템을 불안전한 상태로 만들지 않을 때만 자원을 할당한다.
이게 무슨 말인지 잘 이해가 안 될수도 있는데 최대한 쉽게 흐름을 설명해보겠다.
Need = Max−Allocation위 알고리즘을 통해 데드락을 회피할 수 있다.
데드락을 감지하고 복구할 수 있는 방법이 있다. 주로 데이터베이스 시스템에서 많이 사용된다.
감지 방법은 주기적으로 / 특정 조건에서 자원 할당 그래프를 확인 후 순환 대기 사이클을 찾아내서 데드락을 감지한다. 즉 애플리케이션 중간에 인터럽트를 계산하는 것이다.
복구 방법은 자원 할당을 강제 해제하거나, 하나 이상의 프로세스를 강제 해제하는 방법 등 여러 방법이 있다.
참조
잘해용