Skip to content

高精度定时器的使用

ruki edited this page Dec 21, 2015 · 1 revision

tbox内部提供了两种定时器实现:timer和ltimer

  • timer: 高精度版本,采用最小堆实现,复杂度是:O(log(n))
  • ltimer: 低精度版本,采用linux内核中的timing-wheel算法,复杂度是:O(1)

这里主要讲解下,如何使用timer实现高精度的定时器任务,精确到ms级别,对于低精度的ltimer,可以参考:低精度定时器的使用

下面先给个简单的例子来说明:

/* 定义一个定时器任务处理函数
 *
 * @param killed 表示当前任务是否被tb_timer_task_kill强行kill掉的
 * @param priv   投递任务时传入的用户自定义数据指针
 */
static tb_void_t tb_demo_timer_task_func(tb_bool_t killed, tb_cpointer_t priv)
{
    
}

/* 投递一个定时器任务到全局timer中,间隔1000ms,会重复执行
 *
 * 其中tb_true表示是否会重复执行,如果设为tb_false,那么只会执行一次
 * tb_null参数,就是传入给任务函数的用户自定义数据指针
 */
tb_timer_task_post(tb_timer(), 1000, tb_true, tb_demo_timer_task_func, tb_null);

上面的投递,会在post调用完,立刻开始任务的计时和运行,如果想要在10s后,才开始启动定时器,可以这么投递:

// 在10s后才开始投递一个定时器任务到全局timer中,间隔1000ms,会重复执行
tb_timer_task_post_after(tb_timer(), 10000, 1000, tb_true, tb_demo_timer_task_func, tb_null);

如果要确定绝对投递时间,可以这么投递:

// 在指定时间戳tb_time() + 150000才开始投递一个定时器任务到全局timer中,间隔1000ms,会重复执行
// 这里相当于15s后才开始投递
tb_timer_task_post_at(tb_timer(), tb_time() + 150000, 1000, tb_true, tb_demo_timer_task_func, tb_null);

前面使用的post投递接口,都是无法维护和控制任务的,如果想要在某个特定的时刻取消还在执行队列中的定时器任务

可以使用下面的方式来维护:

// 创建一个间隔10s的定时器任务,不会重复执行
tb_timer_task_ref_t task = tb_timer_task_init(tb_timer(), 10000, tb_false, tb_demo_timer_task_func, tb_null);

// 过段时间后
// ...

// 如果觉得不想执行这个任务了,可以手动取消掉,这个是线程安全的
if (task) tb_timer_task_kill(tb_timer(), task);


// 最后在程序退出时,销毁这个任务资源
if (task) tb_timer_task_exit(tb_timer(), task);

前面的例子都是在全局的默认tb_timer()中投递的定时器任务,tbox会在后台创建一个单独的线程去维护它的所有任务,如果觉得这个太占资源, 自己有特定线程在不断loop的话可以创建个独立的timer,挂接到自己的loop线程中,重用线程资源:

// 假设这个是你自己的线程
static tb_pointer_t tb_demo_timer_loop(tb_cpointer_t priv)
{
    // the timer
    tb_timer_ref_t timer = (tb_ltimer_ref_t)priv;

#if 1
    // 自己的线程loop
    while (1)
    {
        // 等待特定的定时器延时
        // 不一定非得用sleep, 如果你的线程正在处理select/epoll等,可以直接利用这些接口的timeout参数来等待
        tb_sleep(tb_timer_delay(timer));

        // 自己的一些其他逻辑代码
        // ...

        // 固定调用,脉动一下定时器,很快的,不会长时间阻塞,除非有耗时的任务
        tb_timer_spak(timer);
    }
#else
    // 如果没有其他逻辑代码,那么可以直接用timer自带的loop来代替
    tb_timer_loop(timer);
#endif

    // exit it
    tb_thread_return(tb_null);
    return tb_null;
}

/* 创建一个定时器
 *
 * 第一个参数:指定定时器最大并发任务规模,默认可以传0,也可以自己指定规模数
 * 第二个参数:是否启用时间戳缓存优化,传tb_false就行了,这个一般用于服务器端高并发处理时的优化
 */
tb_timer_ref_t timer = tb_timer_init(0, tb_false);
if (timer)
{
    // 投递一些任务
    // ...

    // 退出定时器
    tb_timer_exit(timer);
}
Clone this wiki locally