2025. 2. 22. 18:43ㆍ프로그래밍 언어/C++
📌 4. 비동기 프로그래밍 (std::thread, std::async)
비동기 프로그래밍은 멀티스레딩(multi-threading)과 비동기 작업을 통해 CPU를 효율적으로 사용하는 방법이다.
C++에서는 std::thread, std::async, std::mutex, std::future 등의 기능을 활용하여 멀티스레딩과 비동기 실행을 구현할 수 있다.
📌 4.1 C++에서의 멀티스레딩 개념과 필요성
🔹 (1) 멀티스레딩이란?
멀티스레딩(Multi-threading)은 여러 개의 스레드를 사용하여 동시에 여러 작업을 수행하는 기법이다.
이를 활용하면 CPU의 코어를 최대로 활용할 수 있어, 성능을 향상시킬 수 있다.
💡 멀티스레딩이 필요한 이유
- 멀티코어 CPU 활용 극대화 → 여러 코어에서 동시에 실행 가능
- UI 응답성 개선 → 메인 스레드가 블로킹되지 않도록 설계 가능
- 네트워크 및 파일 I/O 효율화 → 비동기적으로 데이터 처리 가능
💡 예제: 단일 스레드 vs. 멀티스레드
#include <iostream>
#include <thread>
void task() {
for (int i = 0; i < 5; ++i) {
std::cout << "작업 실행 중..." << std::endl;
}
}
int main() {
std::thread t1(task); // 멀티스레드 실행
t1.join(); // 스레드 종료 대기
std::cout << "메인 스레드 종료!" << std::endl;
return 0;
}
🔹 설명
- std::thread를 사용하여 별도의 스레드에서 task() 실행
- t1.join() → 메인 스레드가 작업이 끝날 때까지 대기
📌 4.2 std::thread를 활용한 멀티스레딩 기초
🔹 (1) std::thread를 사용한 스레드 실행
💡 예제: 기본적인 std::thread 사용법
#include <iostream>
#include <thread>
void printMessage() {
std::cout << "별도의 스레드에서 실행 중!" << std::endl;
}
int main() {
std::thread t(printMessage); // 새로운 스레드 실행
t.join(); // 스레드 종료 대기
std::cout << "메인 스레드 종료!" << std::endl;
return 0;
}
🔹 출력 결과
별도의 스레드에서 실행 중!
메인 스레드 종료!
🔹 (2) join() vs. detach()
함수 | 설명 |
join() | 스레드가 종료될 때까지 기다림 (Blocking) |
detach() | 스레드를 백그라운드에서 실행하고 제어 해제 |
💡 예제: detach()를 사용한 백그라운드 스레드
#include <iostream>
#include <thread>
void backgroundTask() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "백그라운드 스레드 완료!" << std::endl;
}
int main() {
std::thread t(backgroundTask);
t.detach(); // 백그라운드에서 실행됨
std::cout << "메인 스레드 종료!" << std::endl;
return 0;
}
🔹 출력 결과
메인 스레드 종료!
(2초 후) 백그라운드 스레드 완료!
📌 4.3 스레드 동기화 (std::mutex, std::lock_guard)
멀티스레딩 환경에서는 여러 스레드가 동시에 같은 데이터에 접근할 경우, 데이터 충돌(경쟁 상태, race condition)이 발생할 수 있다.
이를 방지하기 위해 std::mutex(뮤텍스)를 사용하여 한 번에 하나의 스레드만 특정 코드 블록을 실행하도록 설정할 수 있다.
🔹 (1) std::mutex를 사용한 동기화
💡 예제: std::mutex를 활용한 데이터 보호
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx; // 뮤텍스 선언
int counter = 0;
void increase() {
for (int i = 0; i < 10000; ++i) {
mtx.lock(); // 뮤텍스 잠금
++counter;
mtx.unlock(); // 뮤텍스 해제
}
}
int main() {
std::thread t1(increase);
std::thread t2(increase);
t1.join();
t2.join();
std::cout << "최종 카운터 값: " << counter << std::endl;
return 0;
}
🔹 (2) std::lock_guard를 활용한 자동 잠금 관리
💡 예제: std::lock_guard를 활용한 안전한 동기화
void increase() {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // 뮤텍스 자동 해제
++counter;
}
}
🔹 설명
- std::lock_guard<std::mutex> lock(mtx); → 블록을 벗어나면 자동으로 unlock() 호출됨
📌 4.4 비동기 실행 (std::async, std::future) 개념
멀티스레딩 외에도, 비동기 실행(std::async)을 사용하면 함수 실행 결과를 비동기적으로 받을 수 있다.
💡 예제: std::async를 사용한 비동기 함수 실행
#include <iostream>
#include <future>
int computeSquare(int x) {
return x * x;
}
int main() {
std::future<int> result = std::async(std::launch::async, computeSquare, 5);
std::cout << "결과: " << result.get() << std::endl; // 결과 대기
return 0;
}
🔹 설명
- std::async(std::launch::async, 함수, 인자) → 함수를 비동기적으로 실행
- .get()을 호출하면 결과를 기다림 (필요할 때까지 실행이 블로킹되지 않음)
📌 4.5 C++20의 std::jthread와 자동 종료 기능
C++20에서는 std::thread를 개선한 std::jthread(joining thread)가 도입되었다.
- std::jthread는 소멸될 때 자동으로 join()을 호출하여 자원 누수를 방지할 수 있다.
💡 예제: std::jthread 사용
#include <iostream>
#include <thread>
void task() {
std::cout << "스레드 실행 중..." << std::endl;
}
int main() {
std::jthread t(task); // 자동 join() 기능 포함
return 0;
}
🔹 설명
- std::jthread는 소멸될 때 자동으로 join() 호출하여 개발자가 명시적으로 join()을 호출할 필요가 없다.
📌 4.6 멀티스레딩에서 발생할 수 있는 문제점과 해결 방법
문제점 | 설명 | 해결 방법 |
데드락 (Deadlock) | 두 개 이상의 스레드가 서로의 리소스를 기다리며 무한 대기 | 뮤텍스 사용 순서를 정해 교착 상태 방지 |
경쟁 상태 (Race Condition) | 여러 스레드가 동시에 변수에 접근하여 예측 불가능한 결과 발생 | std::mutex, std::lock_guard를 사용하여 동기화 |
자원 누수 (Resource Leak) | join()을 호출하지 않아 스레드가 종료되지 않음 | std::jthread를 사용하여 자동 join() |
📌 5. 정리
개념 | 설명 |
std::thread | 기본적인 멀티스레딩 지원 |
std::mutex | 동기화를 위한 뮤텍스 |
std::async & std::future | 비동기 실행 지원 |
std::jthread (C++20) | 자동 join() 기능 제공 |
'프로그래밍 언어 > C++' 카테고리의 다른 글
모던 C++(Modern C++)의 정의와 주요 특징 (0) | 2025.03.11 |
---|---|
C++ 초급 - 요약 정리 (0) | 2025.02.22 |
C++ 초급 - 추가 내용 (심화 학습) (3 - 람다 함수 (lambda expressions)) (0) | 2025.02.22 |
C++ 초급 - 추가 내용 (심화 학습) (2 - 파일 입출력 (fstream)) (0) | 2025.02.22 |
C++ 초급 - 추가 내용 (심화 학습) (1 - 네임스페이스 (namespace)) (0) | 2025.02.22 |