프로세스(Process)와 스레드(Thread)

서버란·2024년 9월 18일

CS 지식

목록 보기
8/25

프로세스와 스레드는 운영체제에서 프로그램이 실행될 때 사용하는 기본적인 단위입니다. 둘 다 실행 중인 프로그램을 나타내지만, 관리와 자원 사용 측면에서 중요한 차이점이 있습니다. 이 개념들을 이해하면 시스템에서 작업이 어떻게 실행되고 관리되는지 알 수 있습니다.

1. 프로세스(Process)

프로세스는 운영체제에서 실행 중인 프로그램의 인스턴스입니다. 프로그램이 실행되면 운영체제는 그 프로그램을 위한 독립된 메모리 공간과 시스템 자원을 할당하여 하나의 프로세스를 생성합니다.

특징

  • 독립적인 메모리 공간: 각 프로세스는 고유한 메모리 공간(코드, 데이터, 스택 등)을 가지고 있습니다. 다른 프로세스와 메모리를 공유하지 않으며, 이 때문에 프로세스 간에는 서로의 데이터에 직접 접근할 수 없습니다. 따라서 안전하지만, 프로세스 간 통신(IPC, Inter-Process Communication)이 필요할 때는 별도의 메커니즘을 사용해야 합니다.

  • 자원 관리: 프로세스는 실행에 필요한 시스템 자원(CPU, 메모리, 파일 디스크립터 등)을 운영체제로부터 할당받습니다. 각 프로세스는 고유한 프로세스 ID(PID)를 통해 식별됩니다.

  • 무겁다: 프로세스는 독립적인 자원을 가지므로, 생성 및 종료에 시간이 오래 걸리고 시스템 자원을 많이 사용합니다.

예시
사용자가 텍스트 편집기를 실행하면, 운영체제는 해당 프로그램을 하나의 프로세스로 관리합니다. 이 프로세스는 독립된 메모리 공간을 가지고 있으며, 다른 프로그램의 메모리 공간에 접근하지 않습니다.

2. 스레드(Thread)

스레드는 프로세스 내에서 실행되는 작업의 단위입니다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 이 스레드들은 프로세스의 자원을 공유하면서 동시에 실행될 수 있습니다.

특징

  • 메모리 공유: 스레드는 같은 프로세스 내의 메모리(코드, 데이터, 파일 등)를 공유합니다. 즉, 프로세스 내에서 여러 스레드가 같은 데이터에 접근하고 수정할 수 있습니다. 이로 인해 스레드 간의 통신이 빠르고 효율적이지만, 동기화 문제가 발생할 수 있어 주의해야 합니다.

  • 빠르고 가볍다: 스레드는 독립적인 자원을 갖지 않기 때문에, 프로세스에 비해 생성과 종료가 빠르고 자원을 적게 사용합니다. 다만, 메모리를 공유하기 때문에 동시성(concurrency) 문제(예: 동기화 문제, 레이스 컨디션)가 발생할 수 있습니다.

  • 독립적인 실행 흐름: 스레드는 각자 독립적인 실행 흐름(스택)을 가지며, 동시에 여러 작업을 수행할 수 있습니다. 이를 통해 CPU 활용도를 높일 수 있습니다.

예시
텍스트 편집기에서 파일을 열 때, 하나의 스레드는 파일을 읽고, 다른 스레드는 사용자 입력을 처리하는 작업을 할 수 있습니다. 이렇게 여러 스레드가 동시에 작업을 수행하면 프로그램의 응답성이 높아집니다.

프로세스와 스레드의 차이점

구분프로세스(Process)스레드(Thread)
기본 단위독립적인 실행 단위프로세스 내에서 실행되는 작업 단위
메모리 공유독립된 메모리 공간을 가짐프로세스 내에서 메모리와 자원을 공유
자원 할당CPU, 메모리, 파일 디스크립터 등 자원을 할당받음프로세스의 자원을 공유함
통신 방식프로세스 간 통신(IPC) 필요스레드 간 통신은 빠르고 간단
생성 및 종료생성과 종료에 많은 자원과 시간이 필요생성과 종료가 비교적 빠르고 가벼움
예시웹 브라우저, 텍스트 편집기 등 독립적인 프로그램웹 브라우저에서 여러 탭을 처리하는 스레드

3. 프로세스와 스레드의 상호작용

프로세스는 하나 이상의 스레드를 가질 수 있으며, 스레드가 병렬로 작업을 수행할 수 있게 하여 프로세스의 성능을 높일 수 있습니다. 하지만, 스레드는 메모리와 자원을 공유하기 때문에, 스레드 간의 동기화와 데이터 보호가 중요합니다. 이를 위해 Mutex, Semaphore와 같은 동기화 기법이 사용됩니다.

프로세스와 스레드의 활용

  • 프로세스 활용: 독립적인 프로그램을 실행하고, 서로 다른 프로세스 간에 명확한 경계를 유지할 때 유용합니다. 예를 들어, 시스템에서 독립적으로 동작해야 하는 웹 서버와 데이터베이스 서버는 각기 다른 프로세스로 실행됩니다.

스레드 활용: 동일한 작업을 동시에 처리하거나, 하나의 프로세스 내에서 여러 작업을 병렬로 처리할 때 유용합니다. 예를 들어, 웹 브라우저는 여러 스레드를 사용하여 각 탭을 독립적으로 처리하거나, 동시 다운로드를 처리할 수 있습니다.

4. 멀티스레딩(Multithreading)

멀티스레딩은 하나의 프로세스 내에서 여러 스레드를 병렬로 실행하여 작업을 처리하는 기술입니다. 이를 통해 CPU 자원을 더 효율적으로 사용할 수 있으며, 대규모 계산 작업이나 네트워크 요청 처리와 같은 경우에 유용합니다.

장점

  • 빠른 응답성: 사용자 인터페이스(UI) 프로그램에서는 멀티스레딩을 통해 UI 스레드가 블로킹되지 않고 빠르게 반응할 수 있습니다.
  • 자원 효율성: 같은 프로세스 내에서 자원을 공유하기 때문에, 프로세스 간 통신에 비해 더 적은 자원으로 병렬 처리를 수행할 수 있습니다.
    단점
  • 동기화 문제: 여러 스레드가 동일한 메모리 자원을 접근하고 수정하기 때문에, 동기화 문제(예: 레이스 컨디션)가 발생할 수 있습니다. 이를 해결하기 위해 동기화 메커니즘을 사용해야 합니다.
  • 디버깅 복잡성: 멀티스레딩 프로그램은 오류나 버그를 추적하고 디버깅하는 것이 어려울 수 있습니다. 특히, 동시성 문제는 재현하기 어렵고 특정 환경에서만 발생할 수 있습니다.

Q1. 스레드 간의 데이터 접근이 왜 동기화 문제가 발생할 수 있는지 더 구체적인 예시로 설명해줄 수 있나요?

동기화 문제는 여러 스레드가 동시에 공유 데이터에 접근하고 변경할 때 발생할 수 있습니다. 이 중 가장 대표적인 문제가 레이스 컨디션(Race Condition)입니다. 레이스 컨디션은 두 개 이상의 스레드가 동시에 공유 자원에 접근하여, 예상하지 못한 결과를 초래하는 상황을 말합니다.

예시:
은행 계좌 잔액을 관리하는 코드가 있다고 가정해보겠습니다. 두 스레드가 동시에 같은 계좌의 잔액을 수정하려고 하면 문제가 발생할 수 있습니다.

class Account {
    private int balance = 100;

    public void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }

    public int getBalance() {
        return balance;
    }
}

여기서, 두 스레드가 동시에 withdraw(50)을 호출하면 다음과 같은 문제가 발생할 수 있습니다:

  1. 스레드 1과 스레드 2가 동시에 if (balance >= amount)를 평가하여 참이라고 판단합니다.
  2. 스레드 1이 balance -= 50을 실행하여 잔액을 50으로 줄입니다.
  3. 스레드 2가 balance -= 50을 실행하여 잔액을 0으로 만들어야 하지만, 여전히 100에서 50을 빼서 잔액이 50이 됩니다.

즉, 두 번의 인출이 일어났지만, 실제로는 한 번만 인출된 것처럼 잘못된 결과가 나옵니다. 이 문제를 해결하기 위해서는 동기화(synchronization)가 필요하며, 하나의 스레드가 자원에 접근하는 동안 다른 스레드가 접근하지 못하도록 막아야 합니다. 이를 위해 synchronized 키워드를 사용합니다.

public synchronized void withdraw(int amount) {
    if (balance >= amount) {
        balance -= amount;
    }
}

이렇게 하면, 한 스레드가 자원에 접근하는 동안 다른 스레드는 해당 자원이 사용 가능해질 때까지 대기하게 됩니다.

Q2. 멀티스레딩을 사용하지 않는 것이 더 나은 상황이 있을까요? 그런 상황에서는 왜 멀티스레딩을 피해야 하나요?

멀티스레딩을 사용하지 않는 것이 더 나은 상황도 있습니다. 그 이유는 멀티스레딩이 항상 이점만 있는 것이 아니라, 성능 저하나 복잡성을 증가시킬 수 있기 때문입니다. 멀티스레딩을 피해야 할 상황은 다음과 같습니다:

  1. 단순한 작업: 단일 작업이 매우 간단하고 빠르게 완료된다면, 스레드를 여러 개 생성하는 것 자체가 오버헤드가 될 수 있습니다. 스레드를 생성하고 관리하는 비용이 작업 자체보다 더 클 수 있으므로, 단일 스레드로도 충분히 빠르게 처리할 수 있는 경우 멀티스레딩을 피하는 것이 좋습니다.

  2. I/O 바운드 작업: 네트워크 요청이나 파일 입출력처럼 CPU보다 I/O 성능에 더 의존하는 작업에서는 멀티스레딩이 큰 이점을 주지 못할 수 있습니다. 이런 작업에서는 스레드보다 비동기 방식(예: 비동기 I/O)을 사용하는 것이 더 적합할 수 있습니다.

  3. 동기화 오버헤드가 큰 경우: 멀티스레딩은 동기화가 필요할 때 성능을 저하시킬 수 있습니다. 스레드 간의 자원 공유가 많고, 동기화에 따른 컨텍스트 스위칭 비용이 발생한다면 멀티스레딩이 성능을 떨어뜨릴 수 있습니다. 이 경우 단일 스레드로 처리하거나, 스레드 사용을 최소화하는 방법을 고려할 수 있습니다.

  4. 디버깅과 유지보수 복잡성: 멀티스레드 프로그램은 디버깅이 어렵고, 특히 동시성 문제는 재현이 어려워 버그를 추적하기가 쉽지 않습니다. 이런 복잡성 때문에, 멀티스레딩이 불필요하게 적용된 상황에서는 단일 스레드로 처리하는 것이 유지보수에 유리합니다.

따라서 멀티스레딩을 사용해야 할 때와 사용하지 말아야 할 때를 구분하고, 작업의 성격에 맞는 방법을 선택하는 것이 중요합니다.

Q3. 프로세스 간 통신(IPC)을 효율적으로 처리하기 위한 방법은 어떤 것들이 있을까요?

프로세스 간 통신(IPC, Inter-Process Communication)은 서로 독립된 프로세스들이 데이터를 주고받기 위해 사용되는 메커니즘입니다. IPC를 효율적으로 처리하는 방법에는 여러 가지가 있으며, 각 방법은 특정 상황에 따라 다르게 선택될 수 있습니다.

  1. 파이프(Pipes): 파이프는 한 프로세스가 다른 프로세스로 데이터를 전달하는 가장 간단한 방법입니다. 익명 파이프는 부모-자식 관계의 프로세스 간에만 사용할 수 있으며, 이름 있는 파이프(named pipes)는 서로 다른 프로세스 간에도 사용 가능합니다. 파이프는 단방향 통신을 지원하며, 매우 간단한 데이터 전송에 유리합니다.

  2. 메시지 큐(Message Queue): 메시지 큐는 운영체제가 관리하는 큐를 통해 여러 프로세스가 메시지를 주고받는 방식입니다. 메시지는 특정 형식으로 큐에 저장되며, 프로세스는 큐에서 메시지를 읽고 처리할 수 있습니다. 비동기적으로 메시지를 처리할 수 있어 효율적이며, 여러 프로세스 간 통신이 가능합니다.

  3. 공유 메모리(Shared Memory): 공유 메모리는 두 프로세스가 같은 메모리 공간을 공유하여 데이터를 주고받는 방법입니다. 속도가 매우 빠르며, 큰 데이터를 처리하는 데 적합합니다. 하지만 동시성 문제가 발생할 수 있기 때문에, 동기화 기법(예: 뮤텍스, 세마포어)을 사용해야 합니다.

  4. 소켓(Socket): 소켓은 네트워크 프로토콜을 기반으로 통신하는 방식입니다. 네트워크 상의 다른 컴퓨터와 통신할 때도 사용되지만, 같은 시스템 내의 프로세스 간에도 소켓을 사용할 수 있습니다. 소켓을 사용하면 양방향 통신이 가능하며, 클라이언트-서버 모델에서 주로 사용됩니다.

  5. 세마포어(Semaphore)와 뮤텍스(Mutex): 세마포어와 뮤텍스는 공유 자원에 대한 접근을 제어하기 위해 사용하는 동기화 도구입니다. 두 개 이상의 프로세스가 같은 자원에 접근할 때 동기화를 보장하여 경쟁 상태를 방지할 수 있습니다.

  6. 파일 시스템을 통한 통신: 프로세스 간에 파일을 읽고 쓰는 방식으로 통신할 수도 있습니다. 파일은 모든 프로세스가 접근할 수 있는 공유 자원이므로, 프로세스들이 데이터를 파일에 기록하고 읽음으로써 통신할 수 있습니다. 하지만 파일 시스템을 통한 통신은 속도가 느리고, 실시간 통신에는 적합하지 않을 수 있습니다.

각 방식은 상황에 따라 장단점이 있으며, 시스템 요구사항에 맞는 방법을 선택하는 것이 중요합니다.

profile
백엔드에서 서버엔지니어가 된 사람

0개의 댓글