직관적인느낌

멀티프로세싱과 멀티스레딩: 차이점, 장단점, 그리고 함께 사용하는 방법 본문

공학

멀티프로세싱과 멀티스레딩: 차이점, 장단점, 그리고 함께 사용하는 방법

범슐랭 2024. 9. 20. 08:08
728x90

병렬 프로그래밍은 현대 소프트웨어 개발에서 성능을 최적화하는 중요한 기법입니다. 멀티프로세싱멀티스레딩은 병렬 처리를 구현하는 두 가지 주요 방식입니다. 이 글에서는 이 두 방식의 차이점, 장단점, 그리고 언제 어떤 방식을 선택해야 하는지, 나아가 멀티프로세싱 내에서 멀티스레딩을 함께 사용하는 하이브리드 방식에 대해 설명합니다.


멀티스레딩이란?

**멀티스레딩(Multithreading)**은 하나의 프로세스 내에서 여러 스레드가 동시에 실행되는 구조입니다. 스레드는 같은 메모리 공간을 공유하며, 가벼운 실행 흐름을 가집니다.

장점:

  • 빠른 전환: 스레드 간의 전환이 빠르며, 생성 비용이 낮습니다.
  • 메모리 공유: 스레드는 동일한 메모리 공간을 공유하므로 데이터 교환이 용이합니다.
  • I/O 바운드 작업에 적합: 네트워크 요청, 파일 입출력 등 대기 시간이 많은 작업에서 효율적입니다.

단점:

  • 동기화 문제: 스레드 간의 데이터 충돌을 방지하기 위해 동기화가 필요합니다. 이를 처리하지 않으면 경합 상태나 데드락 같은 문제가 발생할 수 있습니다.
  • 오류 전파: 하나의 스레드에서 문제가 발생하면 전체 프로세스에 영향을 미칩니다.

멀티프로세싱이란?

**멀티프로세싱(Multiprocessing)**은 여러 개의 독립적인 프로세스를 동시에 실행하는 방식입니다. 각 프로세스는 독립적인 메모리 공간을 사용하므로 안정성이 높습니다.

장점:

  • 독립된 메모리 공간: 각 프로세스는 별도의 메모리를 사용하므로 충돌 위험이 적고, 한 프로세스의 오류가 다른 프로세스에 영향을 주지 않습니다.
  • CPU 바운드 작업에 적합: 계산 집약적인 작업에서 여러 CPU 코어를 효율적으로 활용할 수 있습니다.

단점:

  • 비싼 생성 비용: 프로세스 간 전환 비용이 스레드에 비해 큽니다.
  • 데이터 공유가 복잡: 프로세스 간 데이터를 공유하려면 파이프, 소켓, 공유 메모리 등 복잡한 IPC(Inter-Process Communication)를 사용해야 합니다.

멀티프로세싱 vs 멀티스레딩: 언제 어떤 방식을 선택해야 할까?

항목멀티스레딩멀티프로세싱

메모리 사용 프로세스 내 메모리 공유 독립된 메모리 공간 사용
성능 빠르고 경량화된 전환 생성 및 전환 비용이 큼
데이터 공유 쉽게 공유 가능, 하지만 동기화 필요 독립된 메모리, 데이터 공유 복잡
적합한 작업 I/O 바운드 작업 (네트워크, 파일 I/O) CPU 바운드 작업 (계산 집약적 작업)
안정성 스레드 오류 시 전체 프로세스에 영향 프로세스 간 독립적, 오류 전파 없음

하이브리드 방식: 멀티프로세싱과 멀티스레딩 함께 사용하기

병렬 처리를 극대화하기 위해 멀티프로세싱멀티스레딩을 함께 사용하는 방식도 가능합니다. 이 경우, 프로세스는 서로 독립된 메모리를 사용하여 안정성을 유지하고, 각 프로세스 내에서 스레드를 통해 작업을 병렬로 처리할 수 있습니다.

장점:

  • 성능 최적화: CPU 바운드 작업을 프로세스로 분리하고, I/O 바운드 작업을 스레드로 처리하여 성능을 극대화할 수 있습니다.
  • 안정성 확보: 한 프로세스가 실패해도 다른 프로세스에 영향을 주지 않으므로 안정성이 높습니다.

멀티프로세싱과 멀티스레딩을 C++로 구현하는 예제

멀티스레딩 (C++ 코드)

#include <iostream>
#include <thread>
#include <chrono>

void print_numbers() {
    for (int i = 1; i <= 5; ++i) {
        std::cout << "Thread - Number: " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void print_letters() {
    char letters[] = {'A', 'B', 'C', 'D', 'E'};
    for (char letter : letters) {
        std::cout << "Thread - Letter: " << letter << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1500));
    }
}

int main() {
    std::thread thread1(print_numbers);
    std::thread thread2(print_letters);

    thread1.join();
    thread2.join();

    std::cout << "All threads are finished." << std::endl;
    return 0;
}

 

멀티프로세싱과 멀티스레딩 하이브리드 방식 (C++ 코드)

#include <iostream>
#include <thread>
#include <unistd.h> // fork
#include <sys/wait.h> // waitpid

void thread_task1() {
    for (int i = 1; i <= 5; ++i) {
        std::cout << "Thread Task 1 - Number: " << i << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }
}

void thread_task2() {
    for (char letter = 'A'; letter <= 'E'; ++letter) {
        std::cout << "Thread Task 2 - Letter: " << letter << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(700));
    }
}

void run_threads() {
    std::thread t1(thread_task1);
    std::thread t2(thread_task2);
    
    t1.join();
    t2.join();
}

int main() {
    pid_t pid1 = fork();
    if (pid1 == 0) {
        run_threads();
        return 0;
    }

    pid_t pid2 = fork();
    if (pid2 == 0) {
        run_threads();
        return 0;
    }

    waitpid(pid1, nullptr, 0);
    waitpid(pid2, nullptr, 0);

    std::cout << "All processes and threads are finished." << std::endl;
    return 0;
}

 

결론

멀티프로세싱과 멀티스레딩은 병렬 프로그래밍을 구현하는 두 가지 방식으로, 각각의 장단점이 있습니다. I/O 바운드 작업에는 멀티스레딩이, CPU 바운드 작업에는 멀티프로세싱이 적합하지만, 하이브리드 방식으로 이 두 가지를 결합하면 더 높은 성능과 안정성을 얻을 수 있습니다.


728x90
반응형