본문 바로가기
-----ETC-----/C++ 성능 최적화 및 고급 테크닉 시리즈

[C++ 성능 최적화 및 고급 테크닉] Day 16: std::thread와 동기화 기법

by cogito21_cpp 2024. 8. 1.
반응형

std::thread 클래스

std::thread 클래스는 C++11 표준 라이브러리에서 제공하는 멀티스레딩을 위한 클래스입니다. 이를 통해 쉽게 스레드를 생성하고 관리할 수 있습니다.

 

std::thread의 주요 함수

  • 생성자: 새로운 스레드를 생성합니다.
  • join(): 스레드가 종료될 때까지 대기합니다.
  • detach(): 스레드를 분리하여 백그라운드에서 실행되도록 합니다.
  • joinable(): 스레드가 join 또는 detach 가능한 상태인지 확인합니다.

예제 코드

#include <iostream>
#include <thread>

void threadFunction() {
    std::cout << "Hello from thread!" << std::endl;
}

int main() {
    std::thread t(threadFunction); // 스레드 생성
    t.join(); // 메인 스레드가 생성된 스레드가 종료될 때까지 대기
    return 0;
}

 

동기화 기법

여러 스레드가 공유 자원에 접근할 때, 동기화가 필요합니다. C++ 표준 라이브러리는 동기화를 위한 여러 도구를 제공합니다.

 

std::mutex

뮤텍스는 상호 배제를 구현하는 동기화 도구입니다. 뮤텍스를 사용하여 공유 자원에 대한 접근을 동기화할 수 있습니다.

 

예제 코드

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

std::mutex mtx;

void printMessage(const std::string& msg) {
    std::lock_guard<std::mutex> lock(mtx); // 뮤텍스 잠금
    std::cout << msg << std::endl;
}

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

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

    return 0;
}

 

std::lock_guard

std::lock_guard는 뮤텍스를 자동으로 잠그고 해제하는 RAII(Resource Acquisition Is Initialization) 스타일의 동기화 도구입니다.

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

std::mutex mtx;

void safeIncrement(int& counter) {
    std::lock_guard<std::mutex> lock(mtx); // 뮤텍스 잠금
    ++counter;
}

int main() {
    int counter = 0;
    std::thread t1(safeIncrement, std::ref(counter));
    std::thread t2(safeIncrement, std::ref(counter));

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

    std::cout << "Counter value: " << counter << std::endl;
    return 0;
}

 

std::unique_lock

std::unique_lockstd::lock_guard보다 더 유연한 동기화 도구로, 뮤텍스를 잠금/해제하는 시점을 제어할 수 있습니다.

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

std::mutex mtx;

void printMessage(const std::string& msg) {
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << msg << std::endl;
    lock.unlock(); // 뮤텍스 해제
    // 다른 작업 수행
    lock.lock(); // 뮤텍스 다시 잠금
    std::cout << "Finished: " << msg << std::endl;
}

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

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

    return 0;
}

 

std::condition_variable

조건 변수는 특정 조건이 충족될 때까지 스레드를 대기 상태로 유지하는 동기화 도구입니다. 조건 변수는 std::mutex와 함께 사용됩니다.

 

예제 코드

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

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void printMessage() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return ready; }); // ready가 true가 될 때까지 대기
    std::cout << "Hello from thread!" << std::endl;
}

void setReady() {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 잠시 대기
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one(); // 대기 중인 스레드를 깨움
}

int main() {
    std::thread t1(printMessage);
    std::thread t2(setReady);

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

    return 0;
}

 

실습 문제

문제 1: std::thread와 동기화 기법 사용하기

다음 코드에서 std::thread와 동기화 기법을 사용하여 두 개의 스레드가 안전하게 공유 자원에 접근하도록 구현하세요.

 

main.cpp

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

int counter = 0;
std::mutex mtx;

void incrementCounter() {
    for (int i = 0; i < 100000; ++i) {
        counter++;
    }
}

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

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

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

 

해답:

main.cpp (동기화 추가)

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

int counter = 0;
std::mutex mtx;

void incrementCounter() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // 뮤텍스 잠금
        counter++;
    }
}

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

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

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

 

이제 열여섯 번째 날의 학습을 마쳤습니다. std::thread와 동기화 기법을 이해하고, 이를 사용하여 안전한 멀티스레딩을 구현하는 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "병렬 알고리즘과 std::async"에 대해 학습하겠습니다.

반응형