C++ 11:定期调用 C++ 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30425772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ 11: Calling a C++ function periodically
提问by Luca
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:
我已经把一个简单的 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;
};
Now I want to call this from a C++ class as followsL
现在我想从 C++ 类调用它如下L
class Processor()
{
void init()
{
timer.start(25, std::bind(&Processor::process, this));
}
void process()
{
std::cout << "Called" << std::endl;
}
};
However, this calls with the error
但是,这调用错误
terminate called after throwing an instance of 'std::bad_function_call'
what(): bad_function_call
回答by Mikael Persson
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.
代码中的问题是“start”函数中的 lambda 表达式使用[&]
语法通过引用捕获局部变量。这意味着 lambda通过引用捕获interval
和func
变量,它们都是start()
函数的局部变量,因此,它们在从该函数返回后消失。但是,从该函数返回后,lambda 在分离的线程中仍然存在。那时您会收到“错误函数调用”异常,因为它试图func
通过引用不再存在的对象来调用。
What you need to do is capture the local variables by value, with the [=]
syntax on the lambda, as so:
您需要做的是按值捕获局部变量,使用[=]
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();
}
This works when I try it.
这在我尝试时有效。
Or, you could also list out the values you want to capture more explicitly (which I generally recommend for lambdas):
或者,您还可以更明确地列出要捕获的值(我通常建议用于 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();
}
EDIT
编辑
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:
正如其他人指出的那样,使用分离线程并不是一个很好的解决方案,因为您很容易忘记停止线程并且您无法检查它是否已经在运行。此外,您可能应该使_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;
};