我正在使用带有 SIGEV_THREAD 参数的 timer_create 创建 linux 计时器。
有时在我解除定时器并删除它后会调用回调。这会导致段错误,因为它尝试访问已删除的资源。
http://man7.org/linux/man-pages/man2/timer_delete.2.html
Linux手册说
timer_delete() 删除在timerid 中给出ID 的定时器。如果在此呼叫时定时器已布防,则在删除之前将其撤防。被删除的定时器产生的任何未决信号的处理是未指定的。
这基本上意味着我真的不知道回调是否会被调用,我没有办法取消它,也没有办法在清理资源之前强制传递挂起的信号。
class timer_wrapper
{
private:
std::function<void()> callback_;
timer_t timer_;
static void timer_callback(sigval_t val)
{
static_cast<timer_wrapper*>(val.sival_ptr)->callback_();
}
public:
timer_wrapper(std::function<void()> callback, uint32_t interval_sec)
: callback_(std::move(callback))
{
struct sigevent ev;
ev.sigev_notify = SIGEV_THREAD;
ev.sigev_signo = 0;
ev.sigev_value.sival_ptr = this;
ev.sigev_notify_function = &timer_wrapper::timer_callback;
ev.sigev_notify_attributes = 0;
timer_create(CLOCK_REALTIME, &ev, &timer_);
struct itimerspec spec = {{0, 0}, {interval_sec, 0}};
timer_settime(timer_, 0, &spec, nullptr);
}
~timer_wrapper()
{
timer_delete(timer_);
}
};
Run Code Online (Sandbox Code Playgroud)
如果 timer_wrapper 超出范围,我希望回调不会再被调用,但是有时会调用它,根据 man 的说法,这是预期的行为。
解决此问题的建议方法是什么?
删除使用 创建的计时器时避免计时器生成的事件的最简单方法timer_create()是根本不避免它。相反,请使用volatile sig_atomic_t disarmed = 0;标志,并让事件函数在执行其他操作之前测试该标志,如果disarmed非零则立即返回。
这样,您首先设置计时器disarmed,然后删除计时器。
(最好使用原子内置函数,无论是旧式的__sync_fetch_and_add(&disarmed, 0)and __sync_fetch_and_and(&disarmed, 0),还是__atomic_load_n(&disarmed, __ATOMIC_SEQ_CST)and __atomic_exchange_n(&disarmed, 0, __ATOMIC_SEQ_CST),来访问标志,以确保正确的排序。)
对于SIGEV_SIGNAL,您可以首先阻止信号(使用pthread_sigmask()),删除计时器,使用sigtimedwait()零超时检查计时器删除期间是否引发了信号,最后恢复旧的信号掩码。
(我个人使用单个 POSIX 实时信号(SIGRTMIN+0to SIGRTMAX-0,在编译时定义)和以事件时间为键的最小堆(每个堆槽包含时间和对自定义超时/事件结构的引用)来处理大量事件,有专用线程。)
| 归档时间: |
|
| 查看次数: |
782 次 |
| 最近记录: |