【用示例学习与理解C++系列】timerfd与epoll的使用

代码示例

代码运行环境:Android , 开发工具AS,用AS创建一个JIN工程,然后copy示例代码覆盖工程的natvie-lib.cpp文件即可
代码逻辑说明:创建一个首次超时时间2S,后续每间隔50毫秒超时的定时器文件符实例,超时20次后停止

#include <jni.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <sys/epoll.h>
#include <android/log.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>

using namespace std;

static char *TAG = "JNI-DEMO";
static static int MAX_EVENTS_SIZE = 1024;


void demo_timerfd() {
    int timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
    if (timerFd == -1) {
        __android_log_print(ANDROID_LOG_WARN, TAG, "timerfd_create fail");
        return;
    }

    int epollFd = epoll_create(MAX_EVENTS_SIZE);
    if (epollFd == -1) {
        __android_log_print(ANDROID_LOG_WARN, TAG, "epoll_create fail");
        return;
    }
    struct epoll_event ev;
    ev.data.fd = timerFd;
    ev.events = EPOLLIN;
    int ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, timerFd, &ev);
    if (ret == -1) {
        __android_log_print(ANDROID_LOG_WARN, TAG, "epoll_ctl fail");
    }

    struct itimerspec spec;
    //定时器周期超时时间
    spec.it_interval.tv_sec = 0;
    //nsec是纳秒,每50ms超时
    spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
    //定时器首次超时时间,2S后
    spec.it_value.tv_sec = 2;
    spec.it_value.tv_nsec = 0;

    //启动定时间
    if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
        __android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
        return;
    }
    __android_log_print(ANDROID_LOG_INFO, TAG, "start timer");

    thread threadEpoll([epollFd, timerFd] {
        struct epoll_event evs[MAX_EVENTS_SIZE];

        int timeOutCount = 0;
        while (true) {
            int count = epoll_wait(epollFd, evs, MAX_EVENTS_SIZE, -1);
            __android_log_print(ANDROID_LOG_INFO, TAG, "epoll_wait, count:%d", count);
            for (int i = 0; i < count; i++) {
                if (evs[i].data.fd == timerFd && evs[i].events && EPOLLIN) {
                    uint64_t value = 0;
                    int ret = 0;
                    //!!!注意需要作读取的操作,否侧会不停的触发超时/可读事件,只有做了读取的操作才算消费了超时事件
                    ret = read(timerFd, &value, sizeof(uint64_t));
                    __android_log_print(ANDROID_LOG_INFO, TAG, "value:%u, ret:%d", value, ret);
                    timeOutCount++;
                    if (timeOutCount == 19) {
                        struct itimerspec spec;
                        spec.it_interval.tv_sec = 0;
                        spec.it_interval.tv_nsec = 0;
                        spec.it_value.tv_sec = 0;
                        spec.it_value.tv_nsec = 0;
                        if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
                            __android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
                            return;
                        }
                        __android_log_print(ANDROID_LOG_INFO, TAG, "stop timer, timeOutCount:%d", timeOutCount);
                    }
                }
            }
        }
    });
    threadEpoll.detach();
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    demo_timerfd();
    return env->NewStringUTF(hello.c_str());
}

运行效果
在这里插入图片描述

使用注意点

epoll_wait到超时事件后,需要从timefd读数据,否侧可读/超时事件会一直触发!!!!
如上示例代码如果如下读操作的代码删除,会发现定时器似乎不按指定的超时参数工作,一下就epoll_wait到了20次,然后很快执行到了停止定时间的代码

                    ret = read(timerFd, &value, sizeof(uint64_t));

运行结果如下:
在这里插入图片描述

timerfd的说明

timerfd简单的一句话描述就是定时器于文件描述符的形式去使用,即定时器触发超时时会住其绑定的文件描述符写数据,然后我们就可以从文件描述读取出数据,就是专门提供给epoll编程模式下来使用了定时器了
在这里插入图片描述
相关的接口描述更是简结明了:通过文件描述符通知超时事件的定时器!

eventfd的头文件

#include <sys/timerfd.h>
主要是创建timerfd实例,以及设备定时器的启动参数,获取定时器当前的配置参数等

#if __ANDROID_API__ >= 19
int timerfd_create(clockid_t __clock, int __flags) __INTRODUCED_IN(19);
#endif /* __ANDROID_API__ >= 19 */


/** The timerfd_settime() flag to use absolute rather than relative times. */
#define TFD_TIMER_ABSTIME (1 << 0)
/** The timerfd_settime() flag to cancel an absolute timer if the realtime clock changes. */
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)

/**
 * [timerfd_settime(2)](http://man7.org/linux/man-pages/man2/timerfd_settime.2.html) starts or
 * stops a timer.
 *
 * Returns 0 on success, and returns -1 and sets `errno` on failure.
 *
 * Available since API level 19.
 */

#if __ANDROID_API__ >= 19
int timerfd_settime(int __fd, int __flags, const struct itimerspec* __new_value, struct itimerspec* __old_value) __INTRODUCED_IN(19);

/**
 * [timerfd_gettime(2)](http://man7.org/linux/man-pages/man2/timerfd_gettime.2.html) queries the
 * current timer settings.
 *
 * Returns 0 on success, and returns -1 and sets `errno` on failure.
 *
 * Available since API level 19.
 */
int timerfd_gettime(int __fd, struct itimerspec* __current_value) __INTRODUCED_IN(19);
#endif /* __ANDROID_API__ >= 19 */

启动与停止定时器

通过timerfd_settime接口启动与停止定时器,设备所有时间参数为零,即可暂停定时器。注意时间参数是秒 + 纳秒,即定时间的精度可以到纳秒!

    struct itimerspec spec;
    //定时器周期超时时间
    spec.it_interval.tv_sec = 0;
    //nsec是纳秒,每50ms超时
    spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
    //定时器首次超时时间,2S后
    spec.it_value.tv_sec = 2;
    spec.it_value.tv_nsec = 0;

    //启动定时器
    if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
        __android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
        return;
    }

//暂停定时器
  struct itimerspec spec;
  spec.it_interval.tv_sec = 0;
  spec.it_interval.tv_nsec = 0;
  spec.it_value.tv_sec = 0;
  spec.it_value.tv_nsec = 0;
  if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
      __android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
      return;
   }

参考文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值