最近在自测时,需要检测一些代码逻辑的耗时,很简单的做法就是在代码逻辑执行前记录下时间戳,代码逻辑执行后计算当前时间戳与之前所记录时间戳的差值就OK了。为了使得耗时计算逻辑在不同模块的复用,这里对耗时逻辑做了简单的封装,原理很简单,利用了C++类的构造函数以及析构函数来进行自动计算。如下:
time_consuming_detector.h
#ifndef __TIME_CONSUMING_DETECTOR__
#define __TIME_CONSUMING_DETECTOR__
#pragma once
#include <chrono>
#include <functional>
#include <string>
/*
* @brief 耗时检测器(注意:非线程安全)
*/
class TimeConsumingDetector
{
public:
/* 开始回调函数 */
typedef std::function<void(
const std::string& tag,
const std::string& file,
int line,
const std::string& func)> BEGIN_FUNC;
/* 抓拍回调函数 */
typedef std::function<void(
const std::string& tag,
const std::string& subTag,
const std::string& file,
int line,
const std::string& func,
const std::chrono::milliseconds& duration)> CAPTURE_FUNC;
/* 结束回调函数 */
typedef std::function<void(
const std::string& tag,
const std::string& file,
int line,
const std::string& func,
const std::chrono::milliseconds& duration)> END_FUNC;
public:
/**
* @brief 构造函数
* @param beginFunc 开始函数
* @param captureFunc 抓拍函数
* @param endFunc 结束函数
* @param tag 标签
* @param file 调用的文件名
* @param line 调用的行号
* @param func 调用的函数名
*/
TimeConsumingDetector(const BEGIN_FUNC& beginFunc, const CAPTURE_FUNC& captureFunc, const END_FUNC& endFunc,
const std::string& tag = "", const std::string& file = "", int line = 0, const std::string& func = "")
: m_begin(std::chrono::steady_clock::now())
, m_capture(m_begin)
, m_captureFunc(captureFunc)
, m_endFunc(endFunc)
, m_tag(tag)
, m_file(file)
, m_line(line)
, m_func(func)
{
if (beginFunc)
{
beginFunc(tag, file, line, func);
}
}
~TimeConsumingDetector()
{
if (m_endFunc)
{
m_endFunc(m_tag, m_file, m_line, m_func,
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_begin));
}
}
/**
* @brief 抓拍
* @param subTag 子标签
* @param file 调用的文件名
* @param line 调用的行号
* @param func 调用的函数名
* @param captureFunc 抓拍函数(若有设置,则回调该函数,否则回调构造时所设置的函数)
*/
void capture(const std::string& subTag = "", const std::string& file = "", int line = 0, const std::string& func = "",
const CAPTURE_FUNC& captureFunc = nullptr)
{
std::chrono::steady_clock::time_point preCapture = m_capture;
m_capture = std::chrono::steady_clock::now();
std::chrono::milliseconds duration = std::chrono::duration_cast<std::chrono::milliseconds>(m_capture - preCapture);
if (captureFunc)
{
captureFunc(m_tag, subTag, file, line, func, duration);
}
else if (m_captureFunc)
{
m_captureFunc(m_tag, subTag, file, line, func, duration);
}
}
private:
std::chrono::steady_clock::time_point m_begin; /* 开始时间 */
std::chrono::steady_clock::time_point m_capture; /* 抓拍时间 */
CAPTURE_FUNC m_captureFunc; /* 抓拍函数 */
END_FUNC m_endFunc; /* 结束函数 */
std::string m_tag; /* 标签 */
std::string m_file; /* 开始时调用位置的文件名 */
int m_line; /* 开始时调用位置的行号 */
std::string m_func; /* 开始时调用位置的函数名 */
};
#define TIME_CONSUMING_DETECTOR_VAR_NAME_CONNECTION(var1, var2) var1##var2
#define TIME_CONSUMING_DETECTOR_VAR_NAME(var1, var2) TIME_CONSUMING_DETECTOR_VAR_NAME_CONNECTION(var1, var2)
#define TIME_CONSUMINT_DETECTOR_VAR(var) TIME_CONSUMING_DETECTOR_VAR_NAME(var, __LINE__)
/* 定义耗时检测器对象(非线程安全) */
#define TIME_CONSUMING_DETECTOR(detector, beginFunc, captureFunc, endFunc, tag) \
TimeConsumingDetector detector(beginFunc, captureFunc, endFunc, tag, __FILE__, __LINE__, __FUNCTION__);
/* 耗时检测器对象抓拍(非线程安全) */
#define TIME_CONSUMING_DETECTOR_CAPTURE(detector, subTag) \
detector.capture(subTag, __FILE__, __LINE__, __FUNCTION__);
/* 定义带标识的简单耗时检测器对象(非线程安全,自动命名变量) */
#define SIMPLE_TIME_CONSUMING_DETECTOR(endFunc, tag) \
TIME_CONSUMING_DETECTOR(TIME_CONSUMINT_DETECTOR_VAR(_timeConsumingDetector_), nullptr, nullptr, endFunc, tag)
#endif /* __TIME_CONSUMING_DETECTOR__ */
基于耗时检测器,封装了一个超时监控器,如下:
timeout_monitor.h
#ifndef __TIMEOUT_MONITOR__
#define __TIMEOUT_MONITOR__
#pragma once
#include "time_consuming_detector.h"
/*
* @brief 默认的超时监控时长(单位:毫秒)
*/
const auto DEFAULT_TIMEOUT_MONITORING_TIME = std::chrono::milliseconds(50);
/*
* @brief 超时监控器(注意:非线程安全)
*/
class TimeoutMonitor
{
public:
TimeoutMonitor(const TimeConsumingDetector::CAPTURE_FUNC& captureFunc, const TimeConsumingDetector::END_FUNC& endFunc,
const std::chrono::steady_clock::duration& timeout = DEFAULT_TIMEOUT_MONITORING_TIME,
const std::string& tag = "", const std::string& file = "", int line = 0, const std::string& func = "")
{
m_detector = new TimeConsumingDetector(
nullptr, nullptr,
[timeout, endFunc](
const std::string& tag,
const std::string& file,
int line,
const std::string& func,
const std::chrono::milliseconds& duration)
{
if (duration >= timeout)
{
if (endFunc)
{
endFunc(tag, file, line, func, duration);
}
}
}, tag, file, line, func);
m_captureFunc = captureFunc;
}
~TimeoutMonitor()
{
if (m_detector)
{
delete m_detector;
m_detector = nullptr;
}
}
void capture(const std::chrono::steady_clock::duration& timeout = DEFAULT_TIMEOUT_MONITORING_TIME,
const std::string& subTag = "", const std::string& file = "", int line = 0, const std::string& func = "")
{
if (m_detector)
{
m_detector->capture(subTag, file, line, func,
[timeout, captureFunc = m_captureFunc](
const std::string& tag,
const std::string& subTag,
const std::string& file,
int line,
const std::string& func,
const std::chrono::milliseconds& duration)
{
if (duration >= timeout)
{
if (captureFunc)
{
captureFunc(tag, subTag, file, line, func, duration);
}
}
});
}
}
private:
TimeConsumingDetector* m_detector; /* 检测器 */
TimeConsumingDetector::CAPTURE_FUNC m_captureFunc; /* 抓拍函数 */
};
/* 启动超时监控器(非线程安全) */
#define START_TIMEOUT_MONITOR(monitor, captureFunc, endFunc, timeout, tag) \
TimeoutMonitor monitor(captureFunc, endFunc, timeout, tag, __FILE__, __LINE__, __FUNCTION__);
/* 超时监控抓拍(非线程安全) */
#define TIMEOUT_MONITOR_CAPTURE(monitor, timeout, subTag) \
monitor.capture(timeout, subTag, __FILE__, __LINE__, __FUNCTION__);
/* 启动简单超时监控器(非线程安全,自动命名变量) */
#define START_SIMPLE_TIMEOUT_MONITOR(endFunc, timeout, tag) \
START_TIMEOUT_MONITOR(TIME_CONSUMINT_DETECTOR_VAR(_timeoutMonitor_), nullptr, endFunc, timeout, tag)
/* 启动简单超时监控器(非线程安全,自动命名变量,默认指定监控时长) */
#define START_EASY_TIMEOUT_MONITOR(endFunc, tag) START_SIMPLE_TIMEOUT_MONITOR(endFunc, DEFAULT_TIMEOUT_MONITORING_TIME, tag)
#endif /* __TIMEOUT_MONITOR__ */
示例:
#include <iostream>
#include <thread>
#include "timeout_monitor.h"
void onDetectorBegin(const std::string& tag, const std::string& file, int line, const std::string& func)
{
std::cout << "detector[start]: [" << tag << "] [" << file << "] [" << line << "] [" << func << "]" << std::endl;
}
void onDetectorCapture(const std::string& tag, const std::string& subTag, const std::string& file, int line, const std::string& func, const std::chrono::milliseconds& duration)
{
std::cout << "detector[capture]: [" << tag << "][" << subTag << "] [" << file << "] [" << line << "] [" << func << "] [" << duration.count() << " ms]" << std::endl;
}
void onDetectorEnd(const std::string& tag, const std::string& file, int line, const std::string& func, const std::chrono::milliseconds& duration)
{
std::cout << "detector[finish]: [" << tag << "] [" << file << "] [" << line << "] [" << func << "] [" << duration.count() << " ms]" << std::endl;
}
void onMonitorCapture(const std::string& tag, const std::string& subTag, const std::string& file, int line, const std::string& func, const std::chrono::milliseconds& duration)
{
std::cout << "monitor[capture]: [" << tag << "][" << subTag << "] [" << file << "] [" << line << "] [" << func << "] [" << duration.count() << " ms]" << std::endl;
}
void onMonitorEnd(const std::string& tag, const std::string& file, int line, const std::string& func, const std::chrono::milliseconds& duration)
{
std::cout << "monitor[finish]: [" << tag << "] [" << file << "] [" << line << "] [" << func << "] [" << duration.count() << " ms]" << std::endl;
}
void testTimeConsumeDetector1()
{
TIME_CONSUMING_DETECTOR(detector, onDetectorBegin, onDetectorCapture, onDetectorEnd, "testTimeConsumeDetector1");
for (int i = 0; i <= 5; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(i * 100));
TIME_CONSUMING_DETECTOR_CAPTURE(detector, std::to_string(i));
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void testTimeConsumeDetector2()
{
SIMPLE_TIME_CONSUMING_DETECTOR(onDetectorEnd, "testTimeConsumeDetector2");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void testTimeoutMonitor1()
{
START_TIMEOUT_MONITOR(monitor, onMonitorCapture, onMonitorEnd, std::chrono::seconds(1), "testTimeoutMonitor1");
for (int i = 0; i <= 5; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(i * 100));
TIMEOUT_MONITOR_CAPTURE(monitor, std::chrono::milliseconds(50), std::to_string(i));
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void testTimeoutMonitor2()
{
START_SIMPLE_TIMEOUT_MONITOR(onMonitorEnd, std::chrono::milliseconds(100), "testTimeoutMonitor2-1");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
START_EASY_TIMEOUT_MONITOR(onMonitorEnd, "testTimeoutMonitor2-2");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
int main()
{
testTimeConsumeDetector1();
testTimeConsumeDetector2();
std::cout << std::endl << std::endl;
testTimeoutMonitor1();
testTimeoutMonitor2();
return 0;
}