マルチスレッドのプログラムで、スレッドを起動して特定の処理を定期的に実行したいケースはよくあると思う。
時間的な精度を求めない場合はsleepさせれば良いが、ある程度の精度が欲しい場合はインターバルタイマを使用すると思う。
一般的なSIGALRMを使用した割込みでは、待ち受けスレッドが複数ある場合はタイマーが共有されてしまうため、使用できない。
POSIX
timerを使用して、スレッドごとに別のシグナルを割り当てることで実現する事は可能であるが、同じ処理をするスレッドを複数作る場合などは、同じスレッド関数を使いまわしたいから、thread
IDなどでどのスレッドか識別する処理が必要となり、煩雑になる。
スレッドごとのタイマーが欲しいだけなのに、このような実装をするのは無駄に思える。
ネットでは良い方法が見つからなかったので、調べてみた。
signalを用いるのではなく、ファイルディスクリプタを代わりに用いてタイマー満了の通知を行うtimerfdを使用する事で、シグナルハンドラやシグナルのマスクなどの煩雑な作業から解放される。
timerfd_createで作ったファイルディスクリプタをreadすると、それ以降にタイマーが満了するまでブロックされるので、sigwaitの代わりにreadを使う事で、定期的に処理を発生させることが可能となります。
timer
object自体はスレッドごとに管理されているのか否かがmanには明記されていませんが、ファイルディスクリプタに紐づけられているという事はスレッド毎に独立だろうということで、試したところうまく動いたので、ご紹介します。
2つのスレッドを作り、それぞれ1秒と5秒のインターバルタイマーでprint分を定期実行するようにしています。 スレッド異常終了時のクリーンアップハンドラは省略しています。
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/timerfd.h>
#include <pthread.h>
#include <sys/syscall.h>
#define THREAD_MAX 8
(void) {
pid_t gettidreturn syscall(SYS_gettid);
}
void *timer_handler(uint8_t *t){
struct itimerspec val = {*t, 0, *t, 0};
=gettid();
pid_t tidint tfd;
("%d: interval=%hhd\n", tid, *t);
printf
if( (tfd=timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC)) < 0){
("timerfd_create");
perror(EXIT_FAILURE);
exit}
if(timerfd_settime(tfd, 0, &val, NULL) < 0){
("timerfd_settime");
perror(EXIT_FAILURE);
exit}
while(1){
uint64_t times;
if(read(tfd, ×, sizeof(times)) < 0){
("read");
perror}
("%d: Start the work!\n", tid);
printfif(times > 1)
("%d: Warning: Could not finish the work within timer period!\n", tid);
printf}
(tfd);
close}
int main(int argc, char* argv[]) {
[THREAD_MAX];
pthread_t pt
uint8_t time = 1;
if(pthread_create(&pt[0], NULL, (void *)&timer_handler, &time)){
("pthread_create");
perror(EXIT_FAILURE);
exit}
uint8_t time2 = 5;
if(pthread_create(&pt[1], NULL, (void *)&timer_handler, &time2)){
("pthread_create");
perror(EXIT_FAILURE);
exit}
(100);
sleepreturn (EXIT_SUCCESS);
}
実行すると、TID=1264のスレッドが5秒に一回実行されているのが確認できます。
> gcc -o timer -lpthread timer.c
> ./timer
1263: interval=1
1264: interval=5
1263: Start the work!
1263: Start the work!
1263: Start the work!
1263: Start the work!
1263: Start the work!
1264: Start the work!
1263: Start the work!
1263: Start the work!
1263: Start the work!
1263: Start the work!
1264: Start the work!