C++ 다중스레드: 초보자 가이드
안녕하세요, 미래의 코딩 슈퍼스타 여러분! 여러분의 가이드로서 이 흥미로운 C++ 다중스레드 세계로의 여행을 동반하게 되어 기쁩니다. 여러년간 프로그래밍을 가르치는 경험이 있는 저는 여러분에게 보장할 수 있습니다. 이 주제가 처음에는 두려울 수 있지만, 익숙해지면 정말 흥미로운 것이 됩니다. 그럼, 손을 높이며 몸을 던지러 가죠!
다중스레드란 무엇인가요?
자세한 것에 들어가기 전에 기본적인 것부터 시작해보죠. 여러분이 주방에서 복잡한 식사를 준비하는 것을 상상해봅시다. 한 단계씩 하나씩 진행할 수 있습니다 – 채소를 갈고, 그 후 파스타를 삶이고, 마지막으로 소스를 준비합니다. 하지만 이 모든 작업을 동시에 하면 더 효율적이지 않을까요? 이것이 바로 우리의 프로그램에게 다중스레드가 하는 일입니다!
다중스레드는 프로그램이 여러 작업을 동시에 수행할 수 있게 합니다. 각 작업은 "스레드"라고 불립니다. 이는 주방에 여러 주방장이 있는 것과 같고, 각각은 식사의 다른 부분을 책임지는 것입니다.
이제, C++에서 어떻게 이 힘을 활용할 수 있는지 살펴보겠습니다!
스레드 생성
C++에서 스레드를 생성하는 것은 주방에 새로운 주방장을 고용하는 것과 같습니다. 우리는 이 주방장(스레드)에 어떤 작업을 수행하도록 지시해야 합니다. C++에서는 <thread>
라이브러리의 std::thread
클래스를 사용하여 이를 수행합니다.
간단한 예제를 살펴보겠습니다:
#include <iostream>
#include <thread>
void cookPasta() {
std::cout << "Pasta를 삶이는 중..." << std::endl;
}
int main() {
std::thread chefOne(cookPasta);
chefOne.join();
return 0;
}
이 예제에서:
- 필요한 라이브러리를 포함합니다:
<iostream>
는 입력/출력을 위해,<thread>
는 다중스레드를 위해. - 스레드가 실행할 함수
cookPasta()
를 정의합니다. -
main()
에서chefOne
라는 스레드를 생성하고,cookPasta()
함수를 실행하도록 지시합니다. - 프로그램이 끝날 때까지 스레드가 작업을 완료할 때까지 기다리기 위해
join()
를 사용합니다.
이 프로그램을 실행하면 "Pasta를 삶이는 중..."이 콘솔에 출력됩니다. 축하합니다! 여러분은 첫 번째 스레드를 생성했습니다!
스레드 종료
만약 우리의 주방장이 파스타를 삶이는 데 너무 오래 걸린다면 어떻게 될까요? 프로그래밍의 세계에서는 스레드가 작업을 완료하기 전에 종료해야 할 수 있습니다. 그러나 강제로 스레드를 종료하면 자원 누수와 기타 문제를 초래할 수 있습니다. 스레드가 자연스럽게 끝나거나 종료 시그널에 응답하도록 설계하는 것이 일반적으로 더 좋습니다.
다음은 스레드가 종료 시그널에 응답하도록 설정하는 예제입니다:
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<bool> stop_thread(false);
void cookPasta() {
while (!stop_thread) {
std::cout << "Pasta를 여전히 삶이는 중..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Pasta 삶이는 중단!" << std::endl;
}
int main() {
std::thread chefOne(cookPasta);
std::this_thread::sleep_for(std::chrono::seconds(5));
stop_thread = true;
chefOne.join();
return 0;
}
이 예제에서:
-
atomic<bool>
변수stop_thread
를 사용하여 스레드 간 안전하게 통신합니다. -
cookPasta()
함수는 이제 이 변수를 루프에서 확인합니다. -
main()
에서 스레드를 5초 동안 실행한 후,stop_thread
를 true로 설정합니다. - 스레드는 루프를 끝내고 자연스럽게 종료합니다.
스레드에 인수 전달
만약 우리가 주방장에 더 구체적인 지시를 주고 싶다면 어떻게 될까요? C++에서는 함수에 인수를 전달하는 것과 마찬가지로 스레드에 인수를 전달할 수 있습니다. 이를 보여드리겠습니다:
#include <iostream>
#include <thread>
#include <string>
void cookDish(std::string dish, int time) {
std::cout << dish << "를 " << time << "분 동안 삶이는 중." << std::endl;
}
int main() {
std::thread chefOne(cookDish, "스파게티", 10);
std::thread chefTwo(cookDish, "피자", 15);
chefOne.join();
chefTwo.join();
return 0;
}
이 예제에서:
-
cookDish()
함수는 이제 두 개의 매개변수를 받습니다: 식사 이름과 삶이는 시간. - 두 개의 스레드를 생성하여 각각 다른 식사를 다른 시간 동안 삶입니다.
- 스레드를 생성할 때 직접 이 인수를 전달합니다.
이렇게 스레드는 매우 유연하며 – 서로 다른 매개변수로 유사한 작업을 수행할 수 있습니다!
스레드 결합 및 분리
마지막으로, 결합과 분리에 대한 두 가지 중요한 개념에 대해 이야기해보겠습니다.
스레드 결합
이미 우리는 예제에서 join()
을 본 적이 있습니다. 스레드에 join()
을 호출하면, 주 프로그램이 해당 스레드가 끝날 때까지 기다리도록 하는 것입니다. 이는 주방장이 요리를 준비할 때까지 기다리는 것과 같습니다.
스레드 분리
때로는 스레드가 끝날 때까지 기다리지 않고 독립적으로 실행되도록 허용하고 싶을 수 있습니다. 이때 detach()
를 사용합니다. 분리된 스레드는 주 프로그램이 끝난 후에도 배후에서 계속 실행됩니다.
다음은 두 가지 모두를 설명하는 예제입니다:
#include <iostream>
#include <thread>
#include <chrono>
void slowCook(std::string dish) {
std::this_thread::sleep_for(std::chrono::seconds(3));
std::cout << dish << "가 준비되었습니다!" << std::endl;
}
void quickCook(std::string dish) {
std::cout << dish << "가 준비되었습니다!" << std::endl;
}
int main() {
std::thread slowChef(slowCook, "된장");
std::thread quickChef(quickCook, "샐러드");
slowChef.detach(); // 느린 주방장을 배후에서 작업하게 함
quickChef.join(); // 빠른 주방장이 끝날 때까지 기다림
std::cout << "주 프로그램이 끝나고 있습니다. 느린 주방장이 아직 작업 중일 수 있습니다!" << std::endl;
return 0;
}
이 예제에서:
- 두 개의 주방장이 있습니다: 느리게 된장을 삶이는 주방장과 빠르게 샐러드를 준비하는 주방장.
- 느린 주방장의 스레드를 분리하여 배후에서 계속 작업하게 합니다.
- 빠른 주방장의 스레드를 결합하여 샐러드가 준비될 때까지 기다립니다.
- 주 프로그램이 끝나는데, 된장이 준비될 때까지 기다리지 않습니다.
메서드 | 설명 | 사용 사례 |
---|---|---|
join() | 스레드가 완료될 때까지 기다림 | 결과를 필요로 하는 스레드가 끝날 때까지 기다리고 싶을 때 |
detach() | 스레드가 독립적으로 실행됨 | 배후에서 실행되는 작업 |
그리고 이렇게 여러분은 C++ 다중스레드의 첫 걸음을 내딛었습니다. 기억하자, 주방을 배우는 것처럼 다중스레드를 마스터링하는 것도 연습이 필요합니다. 이 개념들을 실험해보고, 곧 마스터 주방장처럼 복잡하고 효율적인 프로그램을 만들 수 있을 것입니다!
코딩을 즐겨, 여러분의 스레드가 항상 원활하게 실행되시길 바랍니다!
Credits: Image by storyset