使用signal和timer相比于使用std::condition_variable和wait的优点在于,更为精确的时间控制和更高的时间精度。使用signal和timer可以实现更为精确的定时器,可以在指定的时间点精确地执行任务,而使用wait函数则存在一定的误差。
正常运行
通过稳定测试,不会有段错误或者其他错误。
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <signal.h>
#include <functional>
#include <memory>
#include <atomic>
#include <condition_variable>
class Timer {
public:
typedef std::function<void()> CallbackFunc;
Timer(int period) : period_(period), timerid_(nullptr), running_(true) {
}
~Timer() {
running_.store(false);
struct itimerspec its;
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid_, 0, &its, NULL) == -1) {
std::cerr << "Failed to set 0 timer." << std::endl;
return;
}
// 删除定时器
if (timerid_ != nullptr) {
if (timer_delete(timerid_) == -1) {
std::cerr << "Failed to delete timer." << std::endl;
return;
}
}
std::cout << "~Timer *************. end" << std::endl;
}
void run() {
std::lock_guard<std::mutex> guard(mtx_);
// 创建定时器
struct sigevent sev;
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_value.sival_ptr = this;
sev.sigev_notify_function = &Timer::timer_handler;
if (timer_create(CLOCK_REALTIME, &sev, &timerid_) == -1) {
std::cerr << "Failed to create timer." << std::endl;
return;
}
// 设置定时器
struct itimerspec its;
its.it_value.tv_sec = period_ / 1000;
its.it_value.tv_nsec = (period_ % 1000) * 1000000;
its.it_interval.tv_sec = period_ / 1000;
its.it_interval.tv_nsec = (period_ % 1000) * 1000000;
if (timer_settime(timerid_, 0, &its, NULL) == -1) {
std::cerr << "Failed to set timer." << std::endl;
return;
}
std::cout << "Timer *************. start" << std::endl;
}
void set_callback(CallbackFunc callback) {
std::lock_guard<std::mutex> guard(mtx_);
callback_ = std::make_shared<CallbackFunc>(callback);
std::cout << "Timer ************* set callback." << std::endl;
}
private:
int period_; // 发布周期(毫秒)
std::mutex mtx_;
timer_t timerid_;
std::atomic_bool running_;
std::shared_ptr<CallbackFunc> callback_;
static void timer_handler(union sigval val) {
Timer *timer = static_cast<Timer*>(val.sival_ptr);
std::lock_guard<std::mutex> guard(timer->mtx_);
if (timer->callback_) {
if (timer->running_.load()) {
//现在的问题是析构函数结束,这里还可能会回调一次回调函数!!!
std::cout << "Timer ************* timer handler callback start." << std::endl;
(*timer->callback_)();//判断何时停止timer
}
}
}
};
class MyClass {
public:
MyClass(int time) : time_(time), timer_(time_), cnt(0) {
timer_.set_callback(std::bind(&MyClass::print_message, this));
timer_.run();
}
void print_message() {
cnt++;
std::cout << "MyClass Publishing... cnt:" << cnt << std::endl;
}
private:
int cnt;
int time_;
Timer timer_;
};
int main()
{
MyClass obj(1000);
// 等待一段时间后停止定时器
std::this_thread::sleep_for(std::chrono::seconds(3));
return 0;
}
程序时序图
程序时序图的简单梳理:
- 主线程开始并创建MyClass对象。
- MyClass对象构造函数中创建Timer对象并设置回调函数。
- Timer对象开始运行并创建定时器。
- 定时器在指定时间到达后触发并调用Timer对象的回调函数。
- Timer对象的回调函数调用MyClass对象的print_message函数。
- print_message函数输出信息并更新计数器。
- 回到第4步,循环执行步骤4到步骤6。
- 主线程等待一段时间后结束程序,MyClass对象和Timer对象自动析构并删除相关资源
排版一
┌───────────────────────────────────────────────────────────────────────────────┐
│ Main │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
Construct obj(1000)
┌───────────────────────────────────────────────────────────────────────────────┐
│ MyClass │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
Construct Timer(1000)
┌───────────────────────────────────────────────────────────────────────────────┐
│ Timer │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
Create Timer
│
├─────────────────────────┐
│ ▼
│ Set Timer
│ │
│ ▼
│ Run Timer
│ │
├─────────────────────────┤
│ ▼
│ Timer Handler
│ │
├─────────────────────────┤
│ ▼
│ Callback(MyClass::print_message)
┌───────────────────────────────────────────────────────────────────────────────┐
│ MyClass │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
Call print_message()
┌───────────────────────────────────────────────────────────────────────────────┐
│ Timer │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
Repeat Steps 4-6
│
├─────────────────────────┐
│ ▼
│ Destructor ~Timer
│ │
│ ▼
│ Delete Timer
│ ▼
│ End Timer Loop
┌───────────────────────────────────────────────────────────────────────────────┐
│ Main │
└────────────────┬──────────────────────────────────────────────────────────────┘
│
▼
End Program
std::condition_variable和wait
使用std::condition_variable和wait的优点在于,更为简单和易于理解。线程间的同步和通信可以通过std::condition_variable和wait函数实现,不需要使用复杂的系统调用,代码更为简洁和易于维护。
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
void publish_thread(int period, std::mutex &mtx, std::condition_variable &cv, bool &done)
{
while (!done) {
// 等待信号
std::unique_lock<std::mutex> lock(mtx);
cv.wait_for(lock, std::chrono::milliseconds(period), [&done]{return done;});
// 执行发布任务
std::cout << "Publishing..." << std::endl;
}
}
int main()
{
int period = 1000; // 发布周期(毫秒)
std::mutex mtx;
std::condition_variable cv;
bool done = false;
std::thread pub_thread(publish_thread, period, std::ref(mtx), std::ref(cv), std::ref(done));
// 发送信号以启动发布线程
while (!done) {
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(period));
}
// 等待发布线程结束
pub_thread.join();
return 0;
}