반응형

어제 면접을 보면서 이 부분에 대한 예시를 들 때, 미흡하였던 부분이 있어 복기하고자 정리합니다.

 

왜이렇게 긴장이 되었는지 아는 내용들도 머리가 하얗게 되어버려서.. 

 

여태 본 면접 중 가장 긴장했던것 같습니다.

그런 김에 다시 리마인드 하고자 정리 고고..


데드락(DeadLock) = 교착 상태

  • 시스템에서 두 개 이상의 프로세스나 스레드가 서로 자원을 기다리며 무한히 대기하는 상태
  • 이로 인해 해당 프로세스나 스레드들이 더 이상 진행할 수 없게 되어 시스템의 일부 또는 전체가 멈추는 현상이 발생합니다.
  • 데드락은 주로 멀티스레드나 멀티프로세스 환경에서 발생합니다.
  • 데드락의 조건
    • 상호 배제(Mutual Exclusion):
      • 자원은 한 번에 한 프로세스만 사용할 수 있습니다.
      • 한 프로세스가 자원을 사용 중이면 다른 프로세스는 그 자원을 사용할 수 없습니다.
    • 점유와 대기(Hold and Wait):
      • 자원을 점유한 프로세스가 다른 자원을 추가로 요청하면서, 이미 점유한 자원을 놓지 않고 대기합니다.
    • 비선점(Non-preemption):
      • 자원이 강제로 뺏기지 않습니다. 한 프로세스가 자원을 점유한 상태에서 다른 프로세스가 그 자원을 요청하면, 점유하고 있는 프로세스가 자원을 자발적으로 놓지 않는 한 자원을 사용할 수 없습니다.
    • 순환 대기(Circular Wait):
      • 자원을 기다리는 프로세스들 간에 순환적인 대기 관계가 형성됩니다.
      • 예를 들어, P1이 P2가 점유한 자원을 기다리고, P2는 P3가 점유한 자원을 기다리고, P3는 다시 P1이 점유한 자원을 기다리는 형태입니다.

데드락 예제

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex1;
std::mutex mutex2;

void thread1() {
    std::lock_guard<std::mutex> lock1(mutex1);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock2(mutex2);
    std::cout << "Thread 1 acquired both mutexes" << std::endl;
}

void thread2() {
    std::lock_guard<std::mutex> lock2(mutex2);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock1(mutex1);
    std::cout << "Thread 2 acquired both mutexes" << std::endl;
}

int main() {
    std::thread t1(thread1);
    std::thread t2(thread2);
    t1.join();
    t2.join();
    return 0;
}

위 코드에서 thread1mutex1 을 먼저 잠그고 mutex2 를 잠그려고 하며, thread2 mutex2 를 먼저 잠그고 mutex1 을 잠그려고 합니다. 이로 인해 두 스레드가 서로 자원을 기다리며 무한히 대기하게 되어 데드락이 발생합니다.


데드락의 해결 방법

데드락을 해결하거나 방지하기 위해 여러 가지 방법이 사용됩니다.

  1. 예방 (Prevention):
    1. 데드락의 4 가지 조건 중 하나를 제거하여 데드락을 예방합니다.
      1. 상호 배제: 자원의 복제본을 만들어 여러 프로세스가 동시에 사용하도록 합니다.
      2. 점유와 대기 방지: 프로세스가 자원을 요청할 때 이미 점유한 자원을 모두 놓도록 합니다.
      3. 비선점 방지: 자원을 요청할 때, 해당 자원을 점유한 프로세스를 강제로 자원을 놓도록 합니다.
      4. 순환 대기 방지: 모든 자원 유형에 대해 순서를 부여하고, 프로세스가 자원을 요청할 때 그 순서대로 요청하도록 합니다.
  2. 회피(Avoidance):
    1. 시스템 상태를 모니터링하여 데드락이 발생할 가능성이 있는 상태를 피합니다.
    2. 대표적인 방법으로 **은행가 알고리즘(Banker's Algorithm)**이 있습니다. 프로세스가 자원을 요청할 때 시스템이 안전한 상태로 남아 있는지 확인하고, 안전하지 않으면 요청을 보류합니다.
  3. 지(Dectection):
    1. 주기적으로 시스템을 검사하여 데드락 상태를 탐지하고, 데드락을 해결하기 위한 조치를 취합니다.
    2. 탐지 후 데드락을 해결하기 위해 자원을 점유한 프로세스를 강제로 종료하거나 자원을 회수하는 방법이 있습니다.
  4. 회복(Recovery):
    1. 데드락이 발생한 후, 이를 해결하기 위한 방법입니다.
    2. 프로세스를 종료하거나, 프로세스가 점유한 자원을 회수하여 데드락을 해결합니다.

결론

데드락은 멀티스레드와 멀티프로세스 환경에서 자원 경쟁으로 인해 발생할 수 있는 심각한 문제입니다.

이를 방지하거나 해결하기 위해서는 데드락의 조건을 이해하고, 적절한 예방, 회피, 탐지, 회복 방법을 사용해야 합니다.

데드락을 효과적으로 관리하면 시스템의 신뢰성과 효율성을 크게 향상시킬 수있습니다.


윗 부분들은 기존 Notion 에 공부하면서 정리해두었던 내용인데,

 

결과적으로 봤을 때는 데드락이 발생한 시점에서는 이것을 해결 하려면 프로세스를 종료 시키고 수정하고 나서 다시 실행해야합니다..

 

처음부터 데드락이 걸리는지 안걸리는지 많은 테스트를 하면서 그게 걸렸는지 탐지하는 기능을 두어야한다는 거죠.

 

그래서 저는 개인적으로 개인 포트폴리오 작업을 할 때, DeadLock Profiler 를 따로 코드 짜서 디버그 모드에서 사용하거나, shared_timed_mutex 를 통해서 동기화 작업을 진행합니다.

 

그것 관련은 추후에 또 올려보겠습니다.. ㅎㅎ

 

 

반응형

+ Recent posts