쓰레드란 Thread
프로세스를 구성하는 독립적인 실행을 보장하는 객체이다. 프로세스의 흐름을 더 작은 단위로 나누고 프로세스에 할당된 자원, 데이터 세그먼트, 힙에 할당된 객체 등을 공유하여 사용할 수 있다.
쓰레드는 독립적인 실행을 보장하므로 자신이 실행하는 함수가 존재한다. 함수의 실행을 위해 자동 변수(쓰레드 로컬) 변수를 사용하기 위한 스택 메모리를 사용하게 된다. 프로세스 단위가 작으므로 문맥 교환에 더 적은 비용이 들기 때문에 멀티 쓰레드 프로그래밍이 자주 사용된다.
운영체제는 여러 쓰레드를 실행하기 위해 CPU의 전체 하드웨어 실행 시간를 나누어 각 쓰레드에게 CPU 사용가능 시간을 부여한다. 이를 타임 퀀텀(time quantum)이라고 한다. 또한 운영체제의 스케줄러가 쓰레드 실행 순서를 결정하는데 이는 운영체제 마다 다르고 반드시 생성한 순서대로 실행이 되는 것도 아니다.
C++ Thread
리눅스의 pthread_create와 Windows의 CreateThread를 대신하여 사용할 수 있는 쓰레드 객체가 C++11 표준에 추가되었다. (C++) thread 객체는 각 운영체제가 지원하는 네이티브 함수(pthread_create, CreateThread)를 사용하여 바탕 쓰레드를 운영하며 높은 추상화 수준으로 C++ 표준 코드로 작성시 운영체제 이식성이 좋다는 장점이 있다.
std::thread
en.cppreference.com/w/cpp/thread/thread
C++ 20에 정의되어 있는 thread 클래스의 일부분이다.
// C++ 20 정의
class thread { // class for observing and managing threads
public:
class id;
using native_handle_type = void*;
thread() noexcept : _Thr{} {}
//...
private:
_Thrd_t _Thr;
}
- id : 쓰레드 id를 지칭한다.
- native_handle_type: 운영체제마다 다르게 구현하는 쓰레드를 사용하는데 이 바탕 쓰레드 핸들의 타입을 지칭한다.
- _Thr: 실제 바탕 쓰레드의 핸들이다.
특수 멤버 함수
- 생성자와 소멸자
쓰레드 객체는 빈 생성자 혹은 함수와 그 인자들을 받아 생성할 수 있다.
// 기본 생성자
thread() noexcept : _Thr{} {}
// public thread 생성자
template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>
_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {
_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
}
// private start 함수
template <class _Fn, class... _Args>
void _Start(_Fn&& _Fx, _Args&&... _Ax) {
using _Tuple = tuple<decay_t<_Fn>, decay_t<_Args>...>;
auto _Decay_copied = _STD make_unique<_Tuple>(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{});
#pragma warning(push)
#pragma warning(disable : 5039) // pointer or reference to potentially throwing function passed to
// extern C function under -EHc. Undefined behavior may occur
// if this function throws an exception. (/Wall)
_Thr._Hnd =
reinterpret_cast<void*>(_CSTD _beginthreadex(nullptr, 0, _Invoker_proc, _Decay_copied.get(), 0, &_Thr._Id));
#pragma warning(pop)
if (_Thr._Hnd) { // ownership transferred to the thread
(void) _Decay_copied.release();
} else { // failed to start thread
_Thr._Id = 0;
_Throw_Cpp_error(_RESOURCE_UNAVAILABLE_TRY_AGAIN);
}
}
_Start함수는 함수 객체와 인자들을 받아 윈도우 운영체제 _beginthreadex 시스템 함수를 내부적으로 사용하고 있다. _beginthreadex는 쓰레드를 생성하고 생성자로 전달받은 함수와 인자들을 가지고 실행 흐름을 만든다.
- 복사/이동 관련
thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}
thread& operator=(thread&& _Other) noexcept {
if (joinable()) {
_STD terminate();
}
_Thr = _STD exchange(_Other._Thr, {});
return *this;
}
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;
void swap(thread& _Other) noexcept {
_STD swap(_Thr, _Other._Thr);
}
기본적으로 복사는 금지되어 있으며 이동 기능만 지원하고 있다.
옵저버 멤버 함수
- joinable: 객체가 합류가능상태인지 반환한다.
- get_id: 쓰레드의 id를 반환한다.
- native_handle: 바탕 쓰레드의 핸들을 반환한다.
- hardware_concurrency: CPU가 실행가능한 동시 쓰레드의 최대 갯수를 반환한다.
연산 멤버 함수
- join: 해당 쓰레드의 함수가 종료될 때까지 기다린다.
- detach: 쓰레드 실행 흐름을 독립적으로 분리한다.
- swap: 두 쓰레드의 소유를 교환한다.
예시)
쓰레드는 호출가능한 모든 객체를 인자로 받아 실행 흐름을 실행할 수 있다.
- 일반 함수
- Functor
- 람다
// function
void sum_func(int &sum, const std::vector<int> &v)
{
for (const int& e : v)
{
sum += e;
}
}
// Functor
class SumFunc
{
public:
void operator()(int& sum, const std::vector<int>& v)
{
for (const int& e : v)
{
sum += e;
}
}
};
int main()
{
std::vector<int> vec;
for (int i = 0; i < 100; ++i)
vec.push_back(i);
int sum1 = 0, sum2 = 0, sum3 = 0;
thread t_sum1(sum_func, std::ref(sum1), vec);
thread t_sum2(SumFunc(), std::ref(sum2), vec);
thread t_sum3([&sum3, &vec]() {for (const int& e : vec) { sum3 += e; }; }); // lambda
t_sum1.join();
t_sum2.join();
t_sum3.join();
printf("%d\n", sum1); //4950
printf("%d\n", sum2); //4950
printf("%d\n", sum3); //4950
return 0;
}
'Computer Science 기본 지식 > 운영체제' 카테고리의 다른 글
[C++ Thread] 공유 변수와 경쟁 조건 (0) | 2021.04.24 |
---|---|
[C++ Thread] C++ Thread 관리하기 (0) | 2021.04.24 |
[운영체제] 가상 메모리 (3) 페이지 교체 알고리즘과 프로세스 적재 정책 (0) | 2021.01.20 |
[운영체제] 가상 메모리 (2) 페이지 부재 (0) | 2021.01.20 |
[운영체제] 가상 메모리 (1) 페이징과 세그먼테이션 (0) | 2021.01.19 |