주기적으로 호출하는 타이머 구현 방법
1. timer_create - SIGEV_THREAD 사용
타이머 1개당 스레드 1개씩 생성됨 대신 시그널을 안씀
(linux)
#include <stdio.h>
#include <thread>
#include <time.h>
#include <signal.h>
#include <sstream>
long long tid()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return std::stoull(ss.str());
}
void on_timer(union sigval sv)
{
//타이머 1개당 스레드 1개임
printf("on_timer : %d, tid: %lld\n", sv.sival_int, tid());
}
timer_t create_timer(int sec)
{
struct sigevent se;
se.sigev_notify = SIGEV_THREAD;
//se.sigev_value.sival_ptr = sec;
se.sigev_value.sival_int = sec;
se.sigev_notify_function = on_timer;
se.sigev_notify_attributes = NULL;
timer_t timer_id = {0};
if (timer_create(CLOCK_REALTIME, &se, &timer_id) != 0) {
return {0};
}
struct itimerspec ts;
ts.it_value.tv_sec = sec;
ts.it_value.tv_nsec = 0;
ts.it_interval.tv_sec = ts.it_value.tv_sec;
ts.it_interval.tv_nsec = ts.it_value.tv_nsec;
if (timer_settime(timer_id, 0, &ts, 0) != 0) {
return {0};
}
return timer_id;
}
int main(void) {
timer_t timer_id1 = create_timer(1);
printf("timer_id : %lld, tid: %lld\n", reinterpret_cast<long long int>(timer_id1), tid());
timer_t timer_id2 = create_timer(2);
printf("timer_id : %lld, tid: %lld\n", reinterpret_cast<long long int>(timer_id2), tid());
char ch = getchar();
printf("getchar : %d\n", ch);
timer_delete(timer_id1);
timer_delete(timer_id2);
return 0;
}
실행
timer_id : 30272848, tid: 139638915491648
timer_id : 30273984, tid: 139638915491648
on_timer : 1, tid: 139638895920896
on_timer : 1, tid: 139638895920896
on_timer : 2, tid: 139638887528192
on_timer : 1, tid: 139638887528192
on_timer : 1, tid: 139638887528192
on_timer : 2, tid: 139638895920896
on_timer : 1, tid: 139638895920896
on_timer : 2, tid: 139638887528192
on_timer : 1, tid: 139638895920896
on_timer : 1, tid: 139638895920896
on_timer : 1, tid: 139638895920896
on_timer : 2, tid: 139638887528192
on_timer : 1, tid: 139638887528192
2. timer_create - SIGEV_SIGNAL 사용
시그널 사용. 항상 main thread 만 사용됨
(linux)
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <cstring>
#include <sstream>
#include <thread>
const int signo = SIGRTMIN; // 사용할 시그널
long long tid()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return std::stoull(ss.str());
}
void on_timer(int sig, siginfo_t* info, void* ucontext)
{
// timer_create를 별도로 해도 항상 main thread에서 실행됨
printf("on_timer : %d, tid: %lld\n", info->si_value.sival_int, tid());
}
timer_t create_timer(int sec)
{
struct sigevent se;
memset(&se, 0x00, sizeof(se));
se.sigev_notify = SIGEV_SIGNAL;
se.sigev_signo = signo;
//se.sigev_value.sival_ptr = sec;
se.sigev_value.sival_int = sec;
timer_t timer_id = {0};
if (timer_create(CLOCK_REALTIME, &se, &timer_id) != 0) {
return {0};
}
struct itimerspec its;
its.it_value.tv_sec = sec;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timer_id, 0, &its, NULL) != 0) {
timer_delete(timer_id);
return {0};
}
return timer_id;
}
bool set_timer_signal()
{
struct sigaction sa;
memset(&sa, 0x00, sizeof(sa));
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = on_timer;
sigemptyset(&sa.sa_mask);
return (sigaction(signo, &sa, NULL) != -1);
}
int main(void) {
set_timer_signal();
timer_t timer_id1 = create_timer(1);
printf("timer_id : %lld, tid: %lld\n", reinterpret_cast<long long int>(timer_id1), tid());
timer_t timer_id2 = create_timer(2);
printf("timer_id : %lld, tid: %lld\n", reinterpret_cast<long long int>(timer_id2), tid());
while (true) {
if (getchar() > 0) {
break;
} else {
printf("interrupted getchar\n"); // getchar return -1
}
}
timer_delete(timer_id1);
timer_delete(timer_id2);
return 0;
}
실행
timer_id : 7994400, tid: 139659796342592
timer_id : 7995472, tid: 139659796342592
on_timer : 1, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
on_timer : 2, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
on_timer : 2, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
interrupted getchar
on_timer : 2, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
on_timer : 2, tid: 139659796342592
interrupted getchar
on_timer : 1, tid: 139659796342592
interrupted getchar
3. CreateTimerQueue
타이머 생성마다 스레드 1개씩 생성.
하지만 CreateTimerQueueTimerd 마지막 flag 인자 값에 따라 다름.
(windows)
#include "stdafx.h"
#include <Windows.h>
#include <thread>
#include <sstream>
long long tid()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return std::stoull(ss.str());
}
void CALLBACK on_timer(void* lpParameter, BOOLEAN TimerOrWaitFired)
{
//CreateTimerQueueTimer 호출당 1스레드 생성
DWORD param = *reinterpret_cast<DWORD*>(lpParameter);
printf("on_timer: %d, tid: %lld\n", param, tid());
}
int main()
{
printf("main thread tid: %lld\n", tid());
HANDLE timer_queue = CreateTimerQueue();
if (timer_queue == NULL) {
printf("error CreateTimerQueue[%u]\n", GetLastError());
}
// https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms682485(v=vs.85).aspx
ULONG flags = 0; // timer 당 1 thread 생성
//ULONG flags = WT_EXECUTEINTIMERTHREAD; // timer 여러개 생성해도 1 thread만 생성
//ULONG flags = WT_EXECUTEONLYONCE,; DWORD period = 0; // 한번만 실행
//1초 마다 on_timer 호출
HANDLE timer1;
DWORD period_msec1 = 1000;
if (!CreateTimerQueueTimer(&timer1, timer_queue, on_timer, &period_msec1, 0, period_msec1, flags)) {
printf("error CreateTimerQueueTimer[%u]\n", GetLastError());
return -1;
}
//1초 마다 on_timer 호출
HANDLE timer2;
DWORD period_msec2 = 2000;
if (!CreateTimerQueueTimer(&timer2, timer_queue, on_timer, &period_msec2, 0, period_msec2, flags)) {
printf("error CreateTimerQueueTimer[%u]\n", GetLastError());
return -1;
}
getchar();
DeleteTimerQueueTimer(timer_queue, timer1, nullptr);
DeleteTimerQueueTimer(timer_queue, timer2, nullptr);
DeleteTimerQueueEx(timer_queue, nullptr);
return 0;
}
실행
main thread tid: 8960
on_timer: 2000, tid: 9672
on_timer: 1000, tid: 9672
on_timer: 1000, tid: 9672
on_timer: 1000, tid: 9672
on_timer: 2000, tid: 268
on_timer: 1000, tid: 268
on_timer: 2000, tid: 9672
on_timer: 1000, tid: 268
on_timer: 1000, tid: 268
on_timer: 2000, tid: 9672
on_timer: 1000, tid: 268
on_timer: 1000, tid: 268
4. SetTimer
console 에서 사용 가능. 항상 main thread 만 사용됨
(windows)
#include "stdafx.h"
#include <Windows.h>
#include <conio.h>
#include <thread>
#include <sstream>
long long tid()
{
std::stringstream ss;
ss << std::this_thread::get_id();
return std::stoull(ss.str());
}
void CALLBACK on_timer(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
// main thread와 동일 스레드에서 동작
printf("on_timer tid: %lld\n", tid());
}
int main()
{
printf("main thread tid: %lld\n", tid());
MSG msg;
UINT_PTR timer_id1 = SetTimer(NULL, 0, 1000, (TIMERPROC)&on_timer);
UINT_PTR timer_id2 = SetTimer(NULL, 0, 2000, (TIMERPROC)&on_timer);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (_kbhit() > 0) {
printf("kill timer and exit program\n");
KillTimer(NULL, timer_id1);
KillTimer(NULL, timer_id2);
break;
}
}
return 0;
}
실행
main thread tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
on_timer tid: 10640
kill timer and exit program
5. 그 외 타이머
Multimedia Timer (windows)
Waitable Timer (windows)
alarm (linux)
참고
http://linkmemo.tistory.com/entry/Win32-%EA%B3%BC%EC%97%B0-%EC%96%B4%EB%96%A4-Timer%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EA%B2%83%EC%9D%B8%EA%B0%80