C++ 11:定期调用C++函数

Luc*_*uca 6 c++ c++11

我已经整理了一个简单的c ++计时器类,它应该定期从SO上的各种示例调用给定的函数,如下所示:

#include <functional>
#include <chrono>
#include <future>
#include <cstdio>

class CallBackTimer
{
public:
    CallBackTimer()
    :_execute(false)
    {}

    void start(int interval, std::function<void(void)> func)
    {
        _execute = true;
        std::thread([&]()
        {
            while (_execute) {
                func();                   
                std::this_thread::sleep_for(
                std::chrono::milliseconds(interval));
            }
        }).detach();
    }

    void stop()
    {
        _execute = false;
    }

private:
    bool            _execute;
};
Run Code Online (Sandbox Code Playgroud)

现在我想从C++类中调用它,如下所示

class Processor()
{
    void init()
    {
         timer.start(25, std::bind(&Processor::process, this));
    }

    void process()
    {
        std::cout << "Called" << std::endl;
    }
};
Run Code Online (Sandbox Code Playgroud)

但是,这会调用错误

terminate called after throwing an instance of 'std::bad_function_call'
what():  bad_function_call
Run Code Online (Sandbox Code Playgroud)

Mik*_*son 21

代码中的问题是你的"start"函数中的lambda表达式使用[&]语法通过引用捕获局部变量.这意味着lambda 通过引用捕获intervalfunc变量,它们都是start()函数的局部变量,因此,它们在从该函数返回后消失.但是,从该函数返回后,lambda在分离的线程内仍然存在.那是当你得到"bad-function-call"异常时,因为它试图func通过引用不再存在的对象进行调用.

您需要做的是使用[=]lambda上的语法按值捕获局部变量,如下所示:

void start(int interval, std::function<void(void)> func)
{
    _execute = true;
    std::thread([=]()
    {
        while (_execute) {
            func();                   
            std::this_thread::sleep_for(
            std::chrono::milliseconds(interval));
        }
    }).detach();
}
Run Code Online (Sandbox Code Playgroud)

这在我尝试时有效.

或者,您还可以更明确地列出要捕获的值(我通常建议使用lambdas):

void start(int interval, std::function<void(void)> func)
{
    _execute = true;
    std::thread([this, interval, func]()
    {
        while (_execute) {
            func();                   
            std::this_thread::sleep_for(
            std::chrono::milliseconds(interval));
        }
    }).detach();
}
Run Code Online (Sandbox Code Playgroud)

编辑

正如其他人所指出的那样,使用分离的线程并不是一个很好的解决方案,因为你很容易忘记停止线程而你无法检查它是否已经在运行.此外,您应该将_execute标志设置为原子,以确保它不会被优化,并且读/写是线程安全的.你可以这样做:

class CallBackTimer
{
public:
    CallBackTimer()
    :_execute(false)
    {}

    ~CallBackTimer() {
        if( _execute.load(std::memory_order_acquire) ) {
            stop();
        };
    }

    void stop()
    {
        _execute.store(false, std::memory_order_release);
        if( _thd.joinable() )
            _thd.join();
    }

    void start(int interval, std::function<void(void)> func)
    {
        if( _execute.load(std::memory_order_acquire) ) {
            stop();
        };
        _execute.store(true, std::memory_order_release);
        _thd = std::thread([this, interval, func]()
        {
            while (_execute.load(std::memory_order_acquire)) {
                func();                   
                std::this_thread::sleep_for(
                std::chrono::milliseconds(interval));
            }
        });
    }

    bool is_running() const noexcept {
        return ( _execute.load(std::memory_order_acquire) && 
                 _thd.joinable() );
    }

private:
    std::atomic<bool> _execute;
    std::thread _thd;
};
Run Code Online (Sandbox Code Playgroud)

  • @Luca我同意其他人的观点,那就是像您一样创建一个分离的线程并不是很好。您应该做的是使线程对象成为CallBackTimer类的数据成员,然后在CallBackTimer类的析构函数中执行_execute = false;之后是thd.join()。同样,您的_execute标志应该是volatile或std :: atomic &lt;bool&gt;,以确保您的while循环不会被优化为while(true)循环。我将进行编辑以显示该内容。 (2认同)