C++线程安全函数计时宏异常:部分场景无输出求助
我正在编写一个用于函数计时的宏,简化示例代码如下:
namespace Tool { static std::mutex timer_mutex{}; static std::map<std::thread::id, unsigned long> timers{}; struct Timer { Timer(const std::string &name); ~Timer(); std::string funcName; std::chrono::high_resolution_clock::time_point time; }; } Tool::Timer::Timer(const std::string &name): funcName(name), time(std::chrono::high_resolution_clock::now()){} { std::lock_guard<std::mutex> lock(timer_mutex); if (timers[std::this_thread::get_id()] < std::numeric_limits<unsigned long>::max()) timers[std::this_thread::get_id()]++; } Tool::Timer::~Timer() { std::lock_guard<std::mutex> lock(timer_mutex); if (timers[std::this_thread::get_id()] > 0) { timers[std::this_thread::get_id()]--; std::cout << getTheTimeNow() - time << "\n"; } } #define TIME_FUNC const Tool::Timer timer(static_cast<const char *>(__PRETTY_FUNCTION__)) void doSomemoreWork() { TIME_FUNC; //some work } int main() { TIME_FUNC; doSomemoreWork(); //doing some work... }
核心逻辑:TIME_FUNC宏会构造Timer结构体,记录当前时间与线程ID;函数结束时Timer对象析构,输出构造与析构的时间差。
目前遇到的问题:有时无法从析构函数中得到std::cout的输出。请问这是数据竞争导致的吗?还是我不应使用宏?
1. 数据竞争的排除
你对timers的访问已经用std::lock_guard做了互斥保护,这部分不会产生数据竞争。std::cout虽然是全局共享资源,但这里的输出操作在互斥锁的保护范围内,同步逻辑是有效的,不会因此丢失输出。
2. 宏的隐患(非输出丢失直接原因)
宏本身不是输出丢失的核心问题,但确实存在风险:
- 无类型检查,若函数内已有同名
timer变量会直接编译失败; __PRETTY_FUNCTION__的格式依赖编译器,跨平台存在差异,但这不会导致输出丢失。
3. 输出丢失的核心原因
(1)构造函数语法错误
你的构造函数写法存在致命问题:初始化列表后的{}是空的构造函数实现,后面的独立代码块不属于构造函数逻辑——也就是说,增加计数的代码根本没在构造函数里执行,完全是无效代码!这会导致timers中线程计数始终为默认值0,析构时timers[std::this_thread::get_id()] > 0的判断不成立,直接跳过输出。
(2)时间差输出逻辑错误
std::chrono::high_resolution_clock::time_point的减法结果是duration类型,无法直接用std::cout输出(除非你重载了operator<<)。如果实际代码中getTheTimeNow()的实现有问题,或者没有正确转换duration为可输出的数值,可能导致编译错误、程序异常,进而看不到输出。
4. 修复建议
(1)修正构造函数语法
把计数逻辑正确放到构造函数体内:
Tool::Timer::Timer(const std::string &name) : funcName(name), time(std::chrono::high_resolution_clock::now()) { std::lock_guard<std::mutex> lock(timer_mutex); auto& count = timers[std::this_thread::get_id()]; if (count < std::numeric_limits<unsigned long>::max()) { count++; } }
(2)正确处理时间差输出
将时间差转换为可输出的数值(比如毫秒),同时替换有问题的getTheTimeNow():
Tool::Timer::~Timer() { std::lock_guard<std::mutex> lock(timer_mutex); auto& count = timers[std::this_thread::get_id()]; if (count > 0) { count--; auto duration = std::chrono::high_resolution_clock::now() - time; auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); std::cout << funcName << " 耗时: " << ms << "ms\n" << std::flush; } }
(3)替换宏为更安全的实现(可选)
用inline函数配合简化宏,避免原始宏的弊端:
inline Tool::Timer CreateTimer(const char* funcName) { return Tool::Timer(funcName); } #define TIME_FUNC const auto timer = CreateTimer(__PRETTY_FUNCTION__)
(4)确保输出缓冲区刷新
多线程环境或程序异常退出时,std::cout的缓冲区可能未刷新,建议用std::flush或std::endl强制刷新,保证输出能及时显示。
内容的提问来源于stack exchange,提问作者Dong

