스레드 타이머는 주 프로그램이 실행되는 메인 스레드와 별개의 스레드에서 일정한 주기마다 주어진 콜백함수를 실행시키는 타이머입니다. C#에선 System.Threading.Timer로 제공하고 있지만 C++에선 직접 만들어 써야 합니다.
STL의 conditional_variable::wait_for()을 통해 매번 루프를 돌리는 풀링을 사용하지 않고 CPU를 자원을 거의 소모하지 않는 타이머를 만들 수 있습니다. 다만 아래 구현에선 타이머 한 개 당 스레드도 한 개가 필요합니다. 그래서 타이머를 너무 많이 만들면 스레드의 스택을 할당하는 과정에서 메모리가 많이 소모될 수 있습니다. 타이머를 많이 만들기 위해 정렬 큐로 여러 타이머를 하나의 스레드에 묶는 방법도 있습니다. 대신에 한 타이머 이벤트 처리가 늦어지면 다른 이벤트도 처리도 같이 늦어집니다. 이런 구현은 다음에 만들어 보도록 하겠습니다.
thread_timer.hpp
#pragma once
#include <thread>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <chrono>
class ThreadTimer {
public:
enum Status {
Stop = 0,
Start = 1,
Start_Once,
Destroyed
};
inline ThreadTimer()
: ThreadTimer(std::chrono::milliseconds(1000), {}) {}
template <class Rep, class Period>
inline ThreadTimer(std::chrono::duration<Rep, Period> interval, std::function<void()> callback, bool start = false)
: status((Status)start), callback(callback), thread(timer_impl, this) {
setInterval(interval);
}
inline ~ThreadTimer() {
status = Destroyed;
cd.notify_one();
thread.join();
}
inline Status getStatus() const {
return status;
}
template <class Rep, class Period>
inline void setInterval(std::chrono::duration<Rep, Period> interval) {
std::lock_guard<std::mutex> guard(mutex_params);
this->interval = interval;
if (status != Stop) cd.notify_one();
}
inline void setCallback(std::function<void()> callback) {
std::lock_guard<std::mutex> guard(mutex_params);
this->callback = callback;
}
inline void start() {
std::lock_guard<std::mutex> guard(mutex_params);
status = Start;
cd.notify_one();
}
inline void start_once() {
std::lock_guard<std::mutex> guard(mutex_params);
status = Start_Once;
cd.notify_one();
}
inline void stop() {
std::lock_guard<std::mutex> guard(mutex_params);
status = Stop;
cd.notify_one();
}
private:
static void timer_impl(ThreadTimer* timer) {
auto& mutex = timer->mutex_cd;
auto& cd = timer->cd;
auto& status = timer->status;
while (status != Destroyed) {
if (status == Start || status == Start_Once) {
std::unique_lock<std::mutex> lock(mutex);
auto res = cd.wait_for(lock, timer->interval);
if (res == std::cv_status::no_timeout) continue;
timer->callback();
if (status == Start_Once) status = Stop;
} else if (status == Stop) {
std::unique_lock<std::mutex> lock(mutex);
cd.wait(lock); // idle
}
}
}
private:
Status status;
std::chrono::nanoseconds interval;
std::function<void()> callback;
std::mutex mutex_params;
std::mutex mutex_cd;
std::condition_variable cd;
std::thread thread;
};
C++아래는 실행 예제입니다.
demo.cpp
#include <iostream>
#include "thread_timer.hpp"
using namespace std;
int main() {
int cnt = 0;
ThreadTimer t0(1000ms, [&](){
cout << "1000ms Elapsed!, cnt = " << ++cnt << "\n";
});
t0.start();
// wait until cnt == 10
while (cnt < 10) {
// do something...
}
return 0;
}
C++
최신 댓글