본문 바로가기
-----ETC-----/C++ 마스터 시리즈

[C++ 마스터] Day 26: 멀티스레딩과 동기화

by cogito21_cpp 2024. 8. 1.
반응형

멀티스레딩 (Multithreading)

멀티스레딩은 하나의 프로그램이 동시에 여러 작업을 수행할 수 있도록 하는 기능입니다. C++11부터 표준 라이브러리는 멀티스레딩을 지원하는 다양한 기능을 제공합니다. 주요 클래스와 함수로는 thread, mutex, lock_guard, unique_lock 등이 있습니다.

 

1. 스레드 (Thread)

스레드는 프로그램의 실행 단위를 나타냅니다. std::thread 클래스를 사용하여 스레드를 생성할 수 있습니다.

 

1.1 스레드 생성

#include <iostream>
#include <thread>

using namespace std;

void printMessage(const string& message) {
    cout << message << endl;
}

int main() {
    thread t1(printMessage, "Hello from thread 1");
    thread t2(printMessage, "Hello from thread 2");

    t1.join();  // 스레드 t1이 종료될 때까지 대기
    t2.join();  // 스레드 t2가 종료될 때까지 대기

    return 0;
}

 

2. 뮤텍스 (Mutex)

뮤텍스는 상호 배제를 제공하여, 여러 스레드가 동시에 공유 자원에 접근하는 것을 방지합니다. std::mutex 클래스를 사용하여 뮤텍스를 생성할 수 있습니다.

 

2.1 뮤텍스 사용

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

using namespace std;

mutex mtx;

void printMessage(const string& message) {
    lock_guard<mutex> guard(mtx);
    cout << message << endl;
}

int main() {
    thread t1(printMessage, "Hello from thread 1");
    thread t2(printMessage, "Hello from thread 2");

    t1.join();
    t2.join();

    return 0;
}

 

3. lock_guard와 unique_lock

lock_guardunique_lock은 RAII 스타일의 뮤텍스 잠금을 제공합니다. lock_guard는 간단한 경우에 사용하고, unique_lock은 더 복잡한 잠금 관리가 필요할 때 사용합니다.

 

3.1 lock_guard 사용

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

using namespace std;

mutex mtx;

void printMessage(const string& message) {
    lock_guard<mutex> guard(mtx);
    cout << message << endl;
}

int main() {
    thread t1(printMessage, "Hello from thread 1");
    thread t2(printMessage, "Hello from thread 2");

    t1.join();
    t2.join();

    return 0;
}

 

3.2 unique_lock 사용

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

using namespace std;

mutex mtx;

void printMessage(const string& message) {
    unique_lock<mutex> lock(mtx);
    cout << message << endl;
    lock.unlock();
    // 다른 작업 수행 가능
    lock.lock();
    // 다시 잠금 가능
}

int main() {
    thread t1(printMessage, "Hello from thread 1");
    thread t2(printMessage, "Hello from thread 2");

    t1.join();
    t2.join();

    return 0;
}

 

예제 문제

문제 1: 스레드를 사용하여 두 개의 함수를 동시에 실행

두 개의 함수를 각각 별도의 스레드에서 실행하고, 두 스레드가 모두 종료된 후 메인 함수가 종료되도록 프로그램을 작성하세요.

 

해설:

#include <iostream>
#include <thread>

using namespace std;

void function1() {
    cout << "Function 1 is running" << endl;
}

void function2() {
    cout << "Function 2 is running" << endl;
}

int main() {
    thread t1(function1);
    thread t2(function2);

    t1.join();
    t2.join();

    cout << "Both threads have finished" << endl;
    return 0;
}

 

이 프로그램은 두 개의 함수를 각각 별도의 스레드에서 실행하고, 두 스레드가 모두 종료된 후 메인 함수가 종료됩니다.

 

문제 2: 뮤텍스를 사용하여 공유 자원의 동기화

두 개의 스레드가 동일한 공유 자원에 접근할 때 뮤텍스를 사용하여 동기화하는 프로그램을 작성하세요. 공유 자원은 정수 변수로 하고, 두 스레드가 번갈아가며 값을 증가시키도록 하세요.

 

해설:

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

using namespace std;

int sharedCounter = 0;
mutex mtx;

void incrementCounter() {
    for (int i = 0; i < 100; ++i) {
        lock_guard<mutex> guard(mtx);
        ++sharedCounter;
    }
}

int main() {
    thread t1(incrementCounter);
    thread t2(incrementCounter);

    t1.join();
    t2.join();

    cout << "Final counter value: " << sharedCounter << endl;
    return 0;
}

 

이 프로그램은 두 개의 스레드가 동일한 공유 자원에 접근할 때 뮤텍스를 사용하여 동기화합니다.

 

문제 3: unique_lock을 사용하여 복잡한 잠금 관리

unique_lock을 사용하여 복잡한 잠금 관리가 필요한 상황을 시뮬레이션하는 프로그램을 작성하세요. 두 개의 스레드가 번갈아가며 공유 자원의 값을 증가시키고, 값을 출력하도록 하세요.

 

해설:

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

using namespace std;

int sharedCounter = 0;
mutex mtx;

void incrementAndPrint() {
    for (int i = 0; i < 100; ++i) {
        unique_lock<mutex> lock(mtx);
        ++sharedCounter;
        cout << "Counter value: " << sharedCounter << endl;
        lock.unlock();
        this_thread::sleep_for(chrono::milliseconds(10));  // 다른 작업 수행 시뮬레이션
        lock.lock();
        // 다시 잠금 후 다른 작업 수행 가능
    }
}

int main() {
    thread t1(incrementAndPrint);
    thread t2(incrementAndPrint);

    t1.join();
    t2.join();

    cout << "Final counter value: " << sharedCounter << endl;
    return 0;
}

 

이 프로그램은 unique_lock을 사용하여 복잡한 잠금 관리를 시뮬레이션합니다.

 

다음 단계

26일차의 목표는 C++의 멀티스레딩과 동기화에 대해 학습하는 것이었습니다. 다음 날부터는 최신 C++ 표준(C++11, C++14, C++17, C++20) 기능 소개에 대해 다룰 것입니다.

 

내일은 "최신 C++ 표준(C++11, C++14, C++17, C++20) 기능 소개"에 대해 다룰 예정입니다. 질문이나 피드백이 있으면 댓글로 남겨 주세요!

반응형