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

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

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

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

    1. /* 定义一个定时器任务处理函数
    2. *
    3. * @param killed 表示当前任务是否被tb_timer_task_kill强行kill掉的
    4. * @param priv 投递任务时传入的用户自定义数据指针
    5. */
    6. static tb_void_t tb_demo_timer_task_func(tb_bool_t killed, tb_cpointer_t priv)
    7. {
    8.  
    9. }
    10.  
    11. /* 投递一个定时器任务到全局timer中,间隔1000ms,会重复执行
    12. *
    13. * 其中tb_true表示是否会重复执行,如果设为tb_false,那么只会执行一次
    14. * tb_null参数,就是传入给任务函数的用户自定义数据指针
    15. */
    16. tb_timer_task_post(tb_timer(), 1000, tb_true, tb_demo_timer_task_func, tb_null);

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

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

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

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

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

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

    1. // 创建一个间隔10s的定时器任务,不会重复执行
    2. tb_timer_task_ref_t task = tb_timer_task_init(tb_timer(), 10000, tb_false, tb_demo_timer_task_func, tb_null);
    3.  
    4. // 过段时间后
    5. // ...
    6.  
    7. // 如果觉得不想执行这个任务了,可以手动取消掉,这个是线程安全的
    8. if (task) tb_timer_task_kill(tb_timer(), task);
    9.  
    10.  
    11. // 最后在程序退出时,销毁这个任务资源
    12. if (task) tb_timer_task_exit(tb_timer(), task);

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

    1. // 假设这个是你自己的线程
    2. static tb_pointer_t tb_demo_timer_loop(tb_cpointer_t priv)
    3. {
    4. // the timer
    5. tb_timer_ref_t timer = (tb_ltimer_ref_t)priv;
    6.  
    7. #if 1
    8. // 自己的线程loop
    9. while (1)
    10. {
    11. // 等待特定的定时器延时
    12. // 不一定非得用sleep, 如果你的线程正在处理select/epoll等,可以直接利用这些接口的timeout参数来等待
    13. tb_sleep(tb_timer_delay(timer));
    14.  
    15. // 自己的一些其他逻辑代码
    16. // ...
    17.  
    18. // 固定调用,脉动一下定时器,很快的,不会长时间阻塞,除非有耗时的任务
    19. tb_timer_spak(timer);
    20. }
    21. #else
    22. // 如果没有其他逻辑代码,那么可以直接用timer自带的loop来代替
    23. tb_timer_loop(timer);
    24. #endif
    25.  
    26. // exit it
    27. tb_thread_return(tb_null);
    28. return tb_null;
    29. }
    30.  
    31. /* 创建一个定时器
    32. *
    33. * 第一个参数:指定定时器最大并发任务规模,默认可以传0,也可以自己指定规模数
    34. * 第二个参数:是否启用时间戳缓存优化,传tb_false就行了,这个一般用于服务器端高并发处理时的优化
    35. */
    36. tb_timer_ref_t timer = tb_timer_init(0, tb_false);
    37. if (timer)
    38. {
    39. // 投递一些任务
    40. // ...
    41.  
    42. // 退出定时器
    43. tb_timer_exit(timer);
    44. }