Files
workflow/docs/about-timer.md
2024-12-24 23:06:45 +08:00

104 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 关于定时器
定时器的作用是不占线程的等待一个确定时间同样通过callback来通知定时器到期。
# 定时器的创建
WFTaskFactory类里包括四个定时相关的接口
~~~cpp
using timer_callback_t = std::function<void (WFTimerTask *)>;
class WFTaskFactory
{
...
public:
static WFTimerTask *create_timer_task(time_t seconds, long nanoseconds,
timer_callback_t callback);
static WFTimerTask *create_timer_task(const std::string& timer_name,
time_t seconds, long nanoseconds,
timer_callback_t callback);
static int cancel_by_name(const std::string& timer_name)
{
cancel_by_name(const std::string& timer_name, (size_t)-1);
}
static int cancel_by_name(const std::string& timer_name, size_t max);
};
~~~
我们通过seconds和nanoseconds两个参数来指定一个定时器的定时时间。其中seconds指定秒数而nanoseconds为纳秒数。
* seconds参数可以传递-1产生一个无限时长的定时器一般用于命名定时器为了将来调用cancel取消定时。
* nanoseconds的取值范围在[0,1000000000)否则timer运行之后会立刻错误返回错误码为EINVAL。
在创建定时器任务时可以传入一个timer_name作为定时器名用于cancel_by_name接口取消定时。
定时器也是一种任务因此使用方式与其它类型任务无异同样有user_data域可以利用。
# 取消定时
如果在创建定时器任务时传入一个名称,那么这个定时器就可以在被提前中断。
中断一个定时任务的方法是通过WFTaskFactory::cancel_by_name这个接口这个接口默认情况下会取消这个名称下的所有定时器。
因此我们也支持传入一个max参数让操作最多取消max个定时器。无论哪个接口返回值都是代表实际被取消的定时器个数。
如果没有这个名称下的定时器cancel操作不会产生任何效果并返回0。
定时器在被创建之后就可取消,并非一定要等它被启动之后。以这个代码为例:
~~~cpp
#include <stdio.h>
#include "workflow/WFTaskFactory.h"
int main()
{
WFTimerTask *timer = WFTaskFactory::create_timer_task("test", 10000, 0, [](WFTimerTask *){
printf("timer callback, state = %d, error = %d.\n", task->get_state(), task->get_error());
});
WFTaskFactory::cancel_by_name("test");
timer->start();
getchar();
return 0;
}
~~~
程序会在立即打印出'timer callback, state = 1, error = 125."因为定时器在运行之前就已经被取消了。所以定时任务启动后立即callback状态码为WFT_STATE_SYS_ERROR错误码为ECANCELED。
使用中需要注意的是命名定时器比匿名定时器是会多出一些开销的原因是我们需要维护查找表会有加锁解锁等操作。如果你的定时器没有提前中断的需要就不要在创建时传入timer_name了。
# 程序退出打断定时器
在[关于程序退出](./about-exit.md)里讲到main函数结束或exit()被调用的时候所有任务必须里运行到callback并且没有新的任务被调起。
这时就可能出现一个问题,定时器的定时周期可以非常长,如果是不可中断的定时器,那么等待定时器到期,程序退出需要很长时间。
而实现上程序退出是可以打断定时器让定时器回到callback的。如果定时器被程序退出打断get_state()会得到一个WFT_STATE_ABORTED状态。
当然如果定时器被程序退出打断,则不能再调起新的任务。
以下这个程序每间隔一秒抓取一个一个http页面。当所有url抓完毕程序直接退出不用等待timer回到callback退出不会有延迟。
~~~cpp
bool program_terminate = false;
void timer_callback(WFTimerTask *timer)
{
mutex.lock();
if (!program_terminate)
{
WFHttpTask *task;
if (urls_to_fetch > 0)
{
task = WFTaskFactory::create_http_task(...);
series_of(timer)->push_back(task);
}
series_of(timer)->push_back(WFTaskFactory::create_timer_task(1, 0, timer_callback));
}
mutex.unlock();
}
...
int main()
{
....
/* all urls done */
mutex.lock();
program_terminate = true;
mutex.unlock();
return 0;
}
~~~
以上程序timer_callback必须在锁里判断program_terminate条件否则可能在程序已经结束的情况下又调起新任务。