실질적인 스레딩 API 자체는 STL에 구현되어 있음.
스레드를 사용하기 위해서는 관련 STL 헤더 포함 필수
<thread> 헤더
std::thread 클래스
std::this_thread 이름 공간 아래의 메소드
- yield
- get_id
- sleep_for
- sleep_until
스레드 클래스
전체 스레딩 API의 핵심이 되는 부분
이 클래스는 하부의 운영체제 스레드를 감싸고 스레드를 시작하고 중지시키는 데에 필요한 기능 제공
선언 방법
함수를 이용한 생성
std::thread 변수(함수명, 전달인자)
클래스 멤버 함수를 이용한 생성
std::thread 변수(클래스명::thread 수행함수, 클래스생성자, 전달인자)
클래스 Static 함수를 이용한 생성
std::thread 변수(클래스명::thread 수행함수, 전달인자)
기본 사용
스레드는 생성과 동시에 즉시 시작
#include <thread>
void worker() {
// Business logic.
}
int main() {
std::thread t(worker);
return 0;
}
위의 코드는 스레드를 시작하고 시작한 스레드의 실행 종류를 대기하지 않음.
따라서 애플리케이션은 즉시 종료
올바르게 사용하기 위해서는 아래와 같이 스레드의 종료를 대기하거나 합류를 대기해야 함.
#include <thread>
void worker() {
// Business logic.
}
int main() {
std::thread t(worker);
t.join();
return 0;
}
위의 코드는 새로운 스레드가 종료하기를 대기한 다음에 합류
인자 전달
새로운 스레드에 인자 전달 가능
인자 값은 이동 가능해야 함. → 이동 또는 복사 생성자를 가지는 유형
#include <thread>
#include <string>
void worker(int n, std::string t) {
// Bunsiness logic.
}
int main () {
std::string s = "Test";
int i = 1;
std::thread t(worker, i, s);
t.join();
return 0;
}
하나의 정수와 문자열을 스레드 함수에 전달
스레드 함수는 이들 두 변수에 대한 복사본을 받음.
반환값
스레드 클래스 생성자에 전달된 함수에 의해 반환된 값 무시
새로운 스레드를 생성한 스레드에 대한 정보를 반환하기 위해 스레드 간의 동기화 매커니즘과 일종의 공유 변수 사용 필요
스레드 이동하기
<utility> 헤더의 std::move 템플릿 메소드를 사용하여 객체 간에 자원 이동 가능
스레드 인스턴스는 이동 가능
#include <thread>
#include <string>
void worker(int n, std::string t) {
// Bunsiness logic.
}
int main () {
std::string s = "Test";
std::thread t(worker, 1, s);
std::thread t1(std::move(t0));
t.join();
return 0;
}
위의 코드에서 다른 스레드로 스레드를 이동하기 전에 한 스레드 생성
즉시 종료되기 때문에 스레드 0은 존재하지 않으며 스레드 실행은 생성된 새로운 스레드에서 재개됨.
첫 번째 스레드가 재합류하기를 대기할 필요가 없고 두 번째 스레드만 대기하면 됨.
스레드 ID
스레드는 자신과 관련된 식별자를 가짐.
이 ID는 STL 구현에 의해 제공되는 고유 식별자
thread 클래스 인스턴스의 get_id() 함수를 호출해 스레드 ID를 구할 수 있음.
std:: this_thread::get_id()를 호출해 함수를 호출하는 스레드의 ID를 구할 수 있음.
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
std::mutex display_mutex
void worker() {
std::thread::id this_id = std::this_thread::get_id();
display_mutex.lock();
std::cout << "thread " << this_id << " sleeping...\n";
display_mutex.unlock();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main() {
std::thread t1(worker);
std::thread::id t1_id = t1.get_id();
std::thread t2(worker);
std::thread::id t2_id = t2.get_id();
display_mutex.lock();
std::cout << "t1's id: " << t1_id << "\n";
std::cout << "t2's id: " << t2_id << "\n";
display_mutex.unlock();
t1.join();
t2.join();
return 0;
}
결과는 아래와 같음.
t1's id: 2
t2's id: 3
thread 2 sleeping...
thread 3 sleeping...
여기서 내부 스레드 ID는 최초 스레드(ID 1)에 상대적인 값을 갖는 정수(std::thread::id 유형)임을 확인 가능
슬립(Sleep)
두 메소드 가운데 하나를 사용하여 스레드의 실행 지연 가능
sleep_for()는 최소 지정된 기간 동안 실행 지연
#include <iostream>
#include <chrono>
#include <thread>
using namespace std::chrono_literals;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> timepoint;
int main() {
std::cout << "Starting sleep.\n";
timepoint start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(1);
timepoint end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Slept for: " << elapsed.count() << " ms\n";
}
위의 코드는 현재 OS에서 가능한 최고 정확도의 카운터를 사용하여 정확한 기간을 측정함으로 대략 2초 동안 슬립하는 방법
초 단위의 수에 s를 붙여서 슬립 시간 직접 지정 가능
<chrono> 헤더의 기능
양보(Yield)
현재 스레드가 재스케줄될 수 있음을 OS에게 알림으로 다른 스레드가 대신 실행되게 함.
std::this_thread::yield() 함수 사용
분리(Detach)
스레드를 시작한 이후에 이 스레드 객체에 대해 detach() 함수 호출 가능
효과적으로 새 스레드를 호출 스레드로부터 분리
새로운 스레드는 호출 스레드가 종료된 이후에도 계속 실행
스왑(Swap)
독립적 메소드 또는 스레드 인스턴스의 함수로서 swap()을 사용하여 스레드 객체의 내부 스레드 핸들 교환 가능
#include <iostream>
#include <thread>
#include <chrono>
void worker() {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main() {
std::thread t1(worker);
std::thread t2(worker);
std::cout << "thread 1 id: " << t1.get_id() << "\n";
std::cout << "thread 2 id: " << t2.get_id() << "\n";
std::swap(t1, t2);
std::cout << "Swapping threads..." << "\n";
std::cout << "thread 1 id: " << t1.get_id() << "\n";
std::cout << "thread 2 id: " << t2.get_id() << "\n";
t1.swap(t2);
std::cout << "Swapping threads..." << "\n";
std::cout << "thread 1 id: " << t1.get_id() << "\n";
std::cout << "thread 2 id: " << t2.get_id() << "\n";
t1.join();
t2.join();
}
결과는 아래와 같음.
thread 1 id: 2
thread 2 id: 3
Swapping threads...
thread 1 id: 3
thread 2 id: 2
Swapping threads...
thread 1 id: 2
thread 2 id: 3
'Language & Tool > C++' 카테고리의 다른 글
05 Visual Studio Code C/C++ 개발 환경 구축 (0) | 2023.07.10 |
---|---|
04 프렌드와 연산자 중복, 상속, 가상 함수와 추상 클래스, 템플릿과 STL (22.08.08) (0) | 2022.12.24 |
03 함수와 참조, 복사 생성자, 함수 중복과 static 멤버 (22.07.25) (0) | 2022.12.24 |
02 클래스와 객체, 객체 포인터, 객체 배열, 객체 동적 생성 (22.07.20) (0) | 2022.12.24 |
01 C++ 프로그래밍의 기본 (22.07.11) (0) | 2022.12.24 |
댓글