Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Linux定时器挂起信号_C++_C_Linux_Timer - Fatal编程技术网

C++ Linux定时器挂起信号

C++ Linux定时器挂起信号,c++,c,linux,timer,C++,C,Linux,Timer,我正在使用timer\u create和SIGEV\u线程参数创建linux计时器 有时在我解除计时器并删除它之后调用回调。这会导致segfault,因为它试图访问已删除的资源 class timer_wrapper { private: std::function<void()> callback_; timer_t timer_; static void timer_callback(sigval_t val) { static_

我正在使用timer\u create和SIGEV\u线程参数创建linux计时器

有时在我解除计时器并删除它之后调用回调。这会导致segfault,因为它试图访问已删除的资源

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_);
    }
};

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_);
    }
};
类计时器包装器 { 私人: std::函数回调; 定时器t定时器; 静态无效计时器回调(sigval\U t val) { 静态_cast(val.sival_ptr)->回调_(); } 公众: 计时器包装(std::函数回调,uint32\u t间隔\u秒) :回调(std::移动(回调)) { 结构sigev; ev.sigev_notify=sigev_线程; ev.sigev_signo=0; ev.sigev_value.sival_ptr=该值; ev.sigev_notify_函数=&timer_包装器::timer_回调; ev.sigev_notify_属性=0; 计时器创建(时钟实时、ev和计时器); struct itimerspec spec={{0,0},{interval_sec,0}; 定时器设置时间(定时器、0和规格、空PTR); } ~timer_wrapper() { 定时器_删除(定时器_); } }; 若计时器包装器超出范围,我预计回调将不再被调用,但它有时会被调用,根据man的说法,这是预期的行为


解决此问题的建议方法是什么?

删除使用创建的计时器时,避免计时器生成事件的最简单方法是根本不避免它。相反,使用
volatile sig_atomic_t disarmed=0标志,并让事件函数在执行任何其他操作之前测试标志,如果
解除防护
为非零,则立即返回

通过这种方式,您首先设置了解除报警
,然后删除计时器

(使用原子内置会更好,要么是旧式的
\uuuu sync\u fetch\u和+u add(&disarmed,0)
\uu sync\u fetch\u和+u and(&disarmed,0)
,要么是
原子加载(&disarmed,\uu atomic\u SEQ\u CST)
\uu原子交换(&disarmed,0,\uu原子SEQ\CST)
,以访问该标志,以确保正确订购。)

对于
SIGEV_信号
,您可以先阻止信号(使用),删除计时器,使用零超时检查计时器删除期间是否发出信号,最后恢复旧的信号掩码


(我个人使用一个POSIX实时信号(
SIGRTMIN+0
SIGRTMAX-0
,在编译时定义),和一个在事件时间上键入的最小堆(每个堆槽包含时间和对自定义超时/事件结构的引用),用专用线程处理大量事件。)

您是否正在检查sigev处理程序中资源的状态?也许您可以设置一个标志来检查正在删除的资源的状态?否,我如何检查?我有一个从sigval_t得到的指针,我不知道它是否被删除。这不容易:)我需要一个存储区来存储flag。如果我清理资源,我还需要清理flag的存储。我可以在回调中清理它,但我不知道它是否会被调用。所以要么是segfault,要么是内存泄漏?为什么不把你的东西发布到MCVE表单中呢?这很简单,但对于单实例计时器来说是可行的。如果我有多个实例,我应该在哪里存储这个原子标志?它应该是成员标志(然后我们遇到同样的问题,标志作为实例的一部分被删除),或者是一种带有原子标志的容器,需要互斥保护,这使得这种方法不那么简单。对于SIGEV_信号,我也可以检查挂起的信号,但我正在尝试为SIGEV_线程找到解决方案。我将考虑更改计时器以防这种情况不起作用。@ NoCuito:删除计时器(使用<代码> TimeReleDeleEe()/Cuff>)必须是一个单独的操作,它删除了C++中的一个代表定时器的对象。使用
SIGEV_-THREAD
时,需要一个宽限期(CPU和挂钟时间),以确保C库在删除计时器之前没有创建新线程,但新线程没有获得运行时间。由于没有确定的方法关闭此竞态窗口,正确的解决方案是使用专用线程和
SIGEV_信号
(信号在所有其他线程中被阻止)。@incognito:注意,您可以使用自己的专用线程处理
SIGEV_信号
轻松地“模拟”
SIGEV_线程
的行为,完全避免比赛窗口。专用计时器线程的唯一问题/困难是将“新计时器”和“取消计时器”处理与超时结合起来。实际上,在请求互斥锁和条件变量上使用
pthread\u cond\u timedwait()
(否则将休眠到下一个超时)而不是一个信号,往往更为健壮。模拟可能可行,但它似乎比标准解决方案更为繁重。无论如何,谢谢。我期待着这个标准的解决方案,但似乎没有解决方案,只有黑客。