在Linux系统中,时间处理是非常重要的,因为许多应用程序需要精确的时间戳来记录事件、进行调度和同步。Linux提供了一系列的时间相关处理函数,这些函数主要位于
延时函数是Linux中比较常用的一类函数,主要包含sleep,usleep,nanosleep函数。
// 相关头文件
#include <unistd.h>
// 用于暂停程序执行指定的秒数
// @second 要暂停的秒数
// @return 如果成功,返回0;如果被信号中断,返回剩余的秒数
unsigned int sleep(unsigned int seconds)
// 使当前进程暂停执行指定的微秒
// @usec 是要暂停的微秒数
// @return 如果成功,返回0;如果被信号中断,返回剩余的秒数
int usleep(useconds_t usec);
struct timespec {
time_t tv_sec; /* 秒 */
long tv_nsec; /* 纳秒 */
};
// 使当前进程暂停执行指定的纳秒数
// req:指向timespec结构的指针,表示要暂停的时间。
// rem:指向timespec结构的指针,用于存储剩余的时间
// 如果成功,返回0。如果被信号中断,返回-1,并设置errno为EINTR。
int nanosleep(const struct timespec *req, struct timespec *rem);
std::condition_variable是同步原语,用于线程间的通讯,它通常与std::mutex和std::unique_lock一起使用。其可以实现超时等待的功能执行,此对象的操作接口如下所示。
// 相关头文件
#include <unistd.h>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
//函数用于阻塞当前线程,直到条件变量被通知。
//lock: 一个 std::unique_lock<std::mutex> 对象,用于锁定互斥量
//pred:一个可调用对象(如函数、函数指针、lambda 表达式等),用于检查等待的条件是否满足
void wait(std::unique_lock<std::mutex>& lock);
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
//用于阻塞当前线程,直到条件变量被通知或指定的时间间隔过去
//lock:用于锁定互斥量
//real_time:一个 std::chrono::duration 对象,表示等待的时间间隔
//pred:一个可调用对象(如函数、函数指针、lambda 表达式等),用于检查等待的条件是否满足
template<class Rep, class Period>
std::cv_status wait_for(std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time);
template<class Rep, class Period, class Predicate>
bool wait_for(std::unique_lock<std::mutex>& lock,
const std::chrono::duration<Rep, Period>& rel_time,
Predicate pred);
//函数用于阻塞当前线程,直到条件变量被通知或到达指定的时间点
//lock:用于锁定互斥量
//abs_time:一个std::chrono::time_point对象,表示等待的截止时间点
//pred:一个可调用对象(如函数、函数指针、lambda 表达式等),用于检查等待的条件是否满足
template<class Clock, class Duration>
std::cv_status wait_until(std::unique_lock<std::mutex>& lock,
const std::chrono::time_point<Clock, Duration>& abs_time);
template<class Clock, class Duration, class Predicate>
bool wait_until(std::unique_lock<std::mutex>& lock,
const std::chrono::time_point<Clock, Duration>& abs_time,
Predicate pred);
// 函数用于唤醒一个正在等待此条件变量的线程
void notify_one() noexcept;
// 函数用于唤醒所有正在等待此条件变量的线程。
void notify_all() noexcept;
基于上述接口,配合std::thread, 可以实现如下超时接口。
#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <functional>
#include <unistd.h>
class TIME_MANAGE_CV
{
public:
TIME_MANAGE_CV() = default;
TIME_MANAGE_CV(const TIME_MANAGE_CV& timer) = delete;
~TIME_MANAGE_CV() {
stop();
}
/// \brief start
/// 启动线程运行,实现定时器功能
/// \param interval - 定时器执行的间隔时间
/// \param task - 定时器执行的回调函数
void start(int interval, std::function<void()> task)
{
if (is_running_)
return;
millisecond_ = interval;
timeout_handler_ = task;
is_running_ = true;
//start thread
std::thread(std::bind(&TIME_MANAGE_CV::run, this)).detach();
}
/// @brief stop
/// 运行停止函数,结束循环执行
void stop() {
if(!is_running_) {
return;
}
is_running_ = false;
cond_.notify_all();
}
private:
/// \brief run
/// 线程执行函数,该函数在独立线程中执行,用于实现超时循环
void run() {
while(is_running_) {
std::unique_lock<std::mutex> lock{mutex_};
auto stauts = cond_.wait_for(lock, std::chrono::milliseconds(millisecond_));
if (stauts == std::cv_status::timeout) {
if (timeout_handler_) {
timeout_handler_();
}
} else {
break;
}
}
std::cout<<"thread finished!\n";
}
private:
/// \brief is_running_
/// - 判断线程释放允许
std::atomic<bool> is_running_ = false;
/// \brief mutex_
/// - mutex_ used for condition.
std::mutex mutex_;
/// \brief cond_
/// - 线程通讯变量
std::condition_variable cond_;
/// \brief timeout_handler_
/// - 超时执行函数
std::function<void()> timeout_handler_;
/// \brief millisecond_
/// - 等待的毫秒超时时间
uint32_t millisecond_{1};
};
int main(int argc, char* argv[])
{
// 创建定时器对象
TIME_MANAGE_CV mcv;
int index = 0;
// 实现定时器和回调函数
mcv.start(1000, [&](){
std::cout<<"cv timer loop: "<<index++<<std::endl;
});
while(1) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (index == 5) {
mcv.stop();
index++;
} else if (index > 5) {
index = 0;
mcv.start(1000, [&](){
std::cout<<"cv new timer loop: "<<index++<<std::endl;
});
}
}
return 0;
}
std::condition_variable 的 wait_for 和 wait_until 方法依赖于系统时间来判断是否超时。如果在这些方法调用期间修改了系统时间,可能会导致以下情况:
而修改系统时间在嵌入式Linux系统中是很可能发生的事,例如NTP时间同步,RTC时间同步,所以使用这个接口目前来看是不可靠的,可以用来了解和属性事件同步,以及定时服务的方法,不建议用于实际产品中。改进方案有使用std::condition_variable_any配合std::chrono::steady_clock不依赖系统时钟(见例程),或者使用posix的超时接口(参考下节)。
class CONDITION_ANY
{
public:
void test(void) {
std::thread task(std::bind(&CONDITION_ANY::run, this));
std::unique_lock<std::mutex> lock(mutex_);
if (cond_.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::seconds(5), [&]{ return ready_; })) {
std::cout << "Singal action." << std::endl;
} else {
std::cout << "Timeout action." << std::endl;
}
task.join();
}
private:
void run() {
std::this_thread::sleep_for(std::chrono::seconds(2));
{
std::lock_guard<std::mutex> lock(mutex_);
ready_ = true;
}
cond_.notify_one();
}
private:
std::mutex mutex_;
std::condition_variable_any cond_;
bool ready_{false};
};
POSIX线程库中也支持线程的条件等待,并允许设置一个超时时间。主要接口包含pthread_mutex_init,pthread_mutex_lock, pthread_mutex_unlock,pthread_condattr_init,pthread_condattr_setclock,pthread_cond_init,pthread_cond_timedwait,pthread_cond_signal,pthread_cond_broadcast,相关操作接口如下所示。
// 相关头文件
#include <pthread.h>
// 初始化一个互斥锁
// @mutex 指向要初始化的互斥锁的指针
// @attr 指向互斥锁属性的指针,通常为NULL,表示使用默认属性。
// @return 成功返回0,失败返回错误码
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
// 锁定一个互斥锁。如果互斥锁已经被锁定,调用线程将阻塞,直到互斥锁被解锁。
// @mutex 指向要锁定的互斥锁的指针
// @return 成功返回0,失败返回错误码。
int pthread_mutex_lock(pthread_mutex_t *mutex);
// 解锁一个互斥锁
// @mutex 指向要解锁的互斥锁的指针
// @return 成功返回0,失败返回错误码。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 初始化一个条件变量属性对象
// @attr 指向要初始化的条件变量属性对象的指针
// @return 成功返回0,失败返回错误码
int pthread_condattr_init(pthread_condattr_t *attr);
// 设置条件变量属性对象的时钟类型
// @attr 指向要设置的条件变量属性对象的指针
// @clock_id 指定的时钟类型,如CLOCK_REALTIME或CLOCK_MONOTONIC
// @return 成功返回0,失败返回错误码
int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id);
// 初始化一个条件变量。
// @cond 指向要初始化的条件变量的指针
// @attr 指向条件变量属性的指针,通常为NULL,表示使用默认属性
// @return 成功返回0,失败返回错误码
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
// 用于线程的条件等待,并允许设置一个超时时间
// @cond:指向一个 pthread_cond_t 类型的条件变量。
// @mutex:指向一个 pthread_mutex_t 类型的互斥锁。
// @abstime:指向一个 struct timespec 类型的结构体,表示绝对时间,即等待的截止时间。
// @return 成功返回0,超时返回ETIMEOUT,被信号中断,返回EINTR
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
// 唤醒一个等待在条件变量上的线程。
// @cond 指向要唤醒的条件变量的指针
// @return 成功返回0,失败返回错误码
int pthread_cond_signal(pthread_cond_t *cond);
// 唤醒所有等待在条件变量上的线程
// @cond 指向要唤醒的条件变量的指针
// @return 成功返回0,失败返回错误码
int pthread_cond_broadcast(pthread_cond_t *cond);
基于上述接口,配合std::thread, 可以实现如下超时接口。
#include <thread>
#include <iostream>
#include <pthread.h>
#include <atomic>
#include <functional>
class TIME_MANAGE_POSIX
{
public:
/// \brief constructor
TIME_MANAGE_POSIX() = default;
TIME_MANAGE_POSIX(const TIME_MANAGE_POSIX &time) = delete;
/// \brief destructor
~TIME_MANAGE_POSIX(){
stop();
pthread_cond_destroy(&m_cond_);
pthread_mutex_destroy(&m_mutex_);
}
/// \brief start
/// - This method is used to start TIME_MANAGE_POSIX thread
/// \param interval - time for the thread run period
/// \param task - task when the thread run.
void start(int interval, std::function<void()> task)
{
pthread_condattr_t attr;
millisecond_ = interval;
timeout_handler_ = task;
pthread_mutex_init(&m_mutex_, NULL);
pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&m_cond_, &attr);
//start thread
std::thread(std::bind(&TIME_MANAGE_POSIX::run, this)).detach();
}
/// \brief stop
/// - This method is used to stop one TIME_MANAGE_POSIX thread
void stop()
{
if (stop_loop_)
return;
stop_loop_ = true;
pthread_mutex_lock(&m_mutex_);
pthread_cond_broadcast(&m_cond_);
pthread_mutex_unlock(&m_mutex_);
}
private:
/// \brief run
/// - This method is used to run the koop by thread
void run()
{
struct timespec ts;
uint64_t millisecond;
stop_loop_ = false;
while (!stop_loop_)
{
clock_gettime(CLOCK_MONOTONIC, &ts);
millisecond = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000) + millisecond_;
ts.tv_sec = millisecond / 1000;
ts.tv_nsec = (millisecond - ((millisecond / 1000) * 1000)) * 1000 * 1000;
pthread_mutex_lock(&m_mutex_);
auto stauts = pthread_cond_timedwait(&m_cond_, &m_mutex_, &ts);
pthread_mutex_unlock(&m_mutex_);
if (stauts == ETIMEDOUT)
{
if (timeout_handler_)
{
timeout_handler_();
}
}
else
{
std::cout<<"TIME_MANAGE_POSIX finished\n";
}
}
}
private:
/// \brief m_cond_
/// - cond used to wait timeout.
pthread_cond_t m_cond_;
/// \brief m_mutex
/// - mutex protect
pthread_mutex_t m_mutex_;
/// \brief millisecond_
/// - ms wait for timeout.
uint64_t millisecond_{1};
/// \brief timeout_handler_
/// - function run when timeout.
std::function<void()> timeout_handler_;
/// \brief stop_loop_
/// - loop flag until stop.
std::atomic<bool> stop_loop_{false};
};
int main(int argc, char* argv[])
{
int index = 0;
TIME_MANAGE_POSIX tmc;
tmc.start(1000, [&](){
std::cout<<"posix timer loop: "<<index++<<std::endl;
});
while(1) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (index == 5) {
tmc.stop();
index++;
} else if (index > 5) {
index = 0;
tmc.start(1000, [&](){
std::cout<<"posix new timer loop: "<<index++<<std::endl;
});
}
}
return 0;
}
直接开始下一节说明: 网络开发调试接口