英文:
C++ Multithreading Callback Timer Function Thread Safety
问题
Suppose I have implemented a callback timer class. The class takes the following arguments:
- a timeout interval
- a function to run
At the end of each timeout period the class spawns a thread and passes it the function to run. The function is not guaranteed to be thread safe.
Further suppose that the timeout interval is shorter than the time it takes the function to finish executing. In this case I would want the "request" to run the callback function to be "queued" so that it runs after the first invocation completes. I do not want two threads to be executing that same function concurrently.
How could I do this using std C++11 supported concurrency libraries?
...Another caveat, what if two timer instantiations are passed the same function?
Example callback:
void myCallback() {
// Do something
std::cout << "Callback function executed.\n";
std::stringstream ss;
int seconds = 5;
for (int i = seconds; i > 0; i--)
{
ss << "Processing data on thread ID: " << std::this_thread::get_id() << ". Ending in " << i << " seconds\n";
std::cout << ss.str();
std::this_thread::sleep_for(std::chrono::seconds(1));
ss.str(std::string());
}
ss << "Thread ID: " << std::this_thread::get_id() << " Finished!\n";
std::cout << ss.str();
}
If I created multiple timer objects on the main thread, and passed the same callback function to each, would I be able to coordinate access to the function across instances?
英文:
Suppose I have implemented a callback timer class. The class takes the following arguments:
- a timeout interval
- a function to run
At the end of each timeout period the class spawns a thread and passes it the function to run. The function is not guaranteed to be thread safe.
Further suppose that the timeout interval is shorter than the time it takes the function to finish executing. In this case I would want the "request" to run the callback function to be "queued" so that it runs after the first invocation completes. I do not want two threads to be executing that same function concurrently.
How could I do this using std C++11 supported concurrency libraries?
...Another caveat, what if two timer instantiations are passed the same function?
Example callback:
void myCallback() {
// Do something
std::cout << "Callback function executed.\n";
std::stringstream ss;
int seconds = 5;
for (int i = seconds; i > 0; i--)
{
ss << "Processing data on thread ID: " << std::this_thread::get_id() << ". Ending in " << i << " seconds\n";
std::cout << ss.str();
std::this_thread::sleep_for(std::chrono::seconds(1));
ss.str(std::string());
}
ss << "Thread ID: " << std::this_thread::get_id() << " Finished!\n";
std::cout << ss.str();
}
If I created multiple timer objects on the main thread, and passed the same callback function to each, would I be able to coordinate access to the function across instances?
答案1
得分: -2
为了使用C++11支持的并发库来实现所需的行为,您可以结合使用std::mutex
、std::condition_variable
和一个标志来指示函数当前是否正在执行。
以下是一个处理请求排队和防止同一函数并发执行的回调定时器类的示例实现:
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
class CallbackTimer {
public:
CallbackTimer(int timeout, std::function<void()> callbackFunc) :
timeout_(timeout),
callbackFunc_(std::move(callbackFunc)),
isRunning_(false) {}
void start() {
std::thread([this]() {
std::unique_lock<std::mutex> lock(mutex_);
while (true) {
// 等待上一次调用完成
while (isRunning_) {
conditionVar_.wait(lock);
}
isRunning_ = true;
lock.unlock();
// 调用回调函数
callbackFunc_();
lock.lock();
isRunning_ = false;
// 通知其他等待的线程函数已经完成
conditionVar_.notify_all();
// 休眠一段时间
std::this_thread::sleep_for(std::chrono::seconds(timeout_));
}
}).detach();
}
private:
int timeout_;
std::function<void()> callbackFunc_;
bool isRunning_;
std::mutex mutex_;
std::condition_variable conditionVar_;
};
在此示例中,CallbackTimer
类使用互斥锁(mutex_
)来同步对共享状态的访问,使用条件变量(conditionVar_
)等待函数调用完成,并使用标志(isRunning_
)来跟踪执行状态。
当启动定时器时,会创建一个新线程并进入无限循环。在循环内部,它首先使用条件变量等待前一次调用完成(while (isRunning_)
)。然后,它将isRunning_
设置为true
,释放锁,并调用回调函数。在函数完成后,它将isRunning_
重置为false
,通知其他等待的线程,然后休眠一段时间,然后再次开始进程。
为了处理两个计时器实例被传递相同函数的情况,每个CallbackTimer
实例都有自己的一组同步对象(mutex_
、conditionVar_
、isRunning_
)。这确保了不同计时器实例之间的状态和执行是隔离的。
您可以如下使用这个CallbackTimer
类:
void myCallback() {
// 做一些操作
std::cout << "Callback function executed." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
int main() {
CallbackTimer timer1(2, myCallback);
CallbackTimer timer2(2, myCallback);
timer1.start();
timer2.start();
// 保持主线程运行
std::this_thread::sleep_for(std::chrono::seconds(10));
return 0;
}
在此示例中,timer1
和 timer2
是CallbackTimer
类的两个实例,都使用相同的 myCallback
函数。这两个计时器同时启动并运行。然而,由于在每个计时器实例内同步执行回调函数,因此两个计时器不会同时执行相同的回调函数。
英文:
To achieve the desired behavior using C++11 supported concurrency libraries, you can make use of a combination of std::mutex
, std::condition_variable
, and a flag to indicate whether the function is currently being executed.
Here's an example implementation of a callback timer class that handles queuing of requests and prevents concurrent execution of the same function:
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
class CallbackTimer {
public:
CallbackTimer(int timeout, std::function<void()> callbackFunc) :
timeout_(timeout),
callbackFunc_(std::move(callbackFunc)),
isRunning_(false) {}
void start() {
std::thread([this]() {
std::unique_lock<std::mutex> lock(mutex_);
while (true) {
// Wait until the previous invocation completes
while (isRunning_) {
conditionVar_.wait(lock);
}
isRunning_ = true;
lock.unlock();
// Invoke the callback function
callbackFunc_();
lock.lock();
isRunning_ = false;
// Notify other waiting threads that the function has completed
conditionVar_.notify_all();
// Sleep for the timeout period
std::this_thread::sleep_for(std::chrono::seconds(timeout_));
}
}).detach();
}
private:
int timeout_;
std::function<void()> callbackFunc_;
bool isRunning_;
std::mutex mutex_;
std::condition_variable conditionVar_;
};
In this example, the CallbackTimer
class uses a mutex (mutex_
) to synchronize access to the shared state, a condition variable (conditionVar_
) to wait for the function invocation to complete, and a flag (isRunning_
) to track the state of execution.
When the timer is started, a new thread is created and enters an infinite loop. Inside the loop, it first waits until the previous invocation completes (while (isRunning_)
) using the condition variable. Then, it sets isRunning_
to true
, releases the lock, and invokes the callback function. After the function completes, it resets isRunning_
to false
, notifies other waiting threads, and sleeps for the timeout period before starting the process again.
To handle the scenario where two timer instantiations are passed the same function, each CallbackTimer
instance has its own set of synchronization objects (mutex_
, conditionVar_
, isRunning_
). This ensures that the state and execution are isolated between different timer instances.
You can use this CallbackTimer
class as follows:
void myCallback() {
// Do something
std::cout << "Callback function executed." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
int main() {
CallbackTimer timer1(2, myCallback);
CallbackTimer timer2(2, myCallback);
timer1.start();
timer2.start();
// Keep the main thread running
std::this_thread::sleep_for(std::chrono::seconds(10));
return 0;
}
In this example, timer1
and timer2
are two instances of the CallbackTimer
class, both using the same myCallback
function. The timers are started and run concurrently. However, since the callback function execution is synchronized within each timer instance, the two timers will not execute the same callback function concurrently.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论