C ++ 11:定期调用C ++函数 [英] C++ 11: Calling a C++ function periodically

查看:85
本文介绍了C ++ 11:定期调用C ++函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have put together a simple c++ timer class that is supposed to call a given function periodically from various examples on SO as follows:

#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;
};

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

Now I want to call this from a C++ class as followsL

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

    void process()
    {
        std::cout << "Called" << std::endl;
    }
};

但是,这会出现错误

terminate called after throwing an instance of 'std::bad_function_call'
what():  bad_function_call


推荐答案

代码中的问题是, start函数中的lambda表达式使用<$ c $通过引用捕获局部变量。 c> [&] 语法。这意味着lambda通过引用捕获 interval func 变量,它们都是<$ c的局部变量$ c> start()函数,因此,它们从该函数返回后便消失了。但是,从该函数返回后,lambda仍然在分离的线程中仍然存在。那是当您收到错误函数调用异常的时候,因为它试图通过引用不再存在的对象来调用 func

The problem in your code is that your lambda expression inside your "start" function captures the local variables by reference, using the [&] syntax. This means that the lambda captures the interval and func variables by reference, which are both local variables to the start() function, and thus, they disappear after returning from that function. But, after returning from that function, the lambda is still alive inside the detached thread. That's when you get the "bad-function-call" exception because it tries to call func by reference to an object that no longer exists.

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

What you need to do is capture the local variables by value, with the [=] syntax on the lambda, as so:

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

在我尝试时可以使用。

或者,您也可以更明确地列出要捕获的值(我通常推荐给lambda):

Or, you could also list out the values you want to capture more explicitly (which I generally recommend for 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();
}

编辑

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

As others have pointed out, the use of a detached thread is not a great solution because you could easily forget to stop the thread and you have no way to check if it's already running. Also, you should probably make the _execute flag atomic, just to be sure it doesn't get optimized out and that the reads / writes are thread-safe. You could do this instead:

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

这篇关于C ++ 11:定期调用C ++函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆