You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 08:45:02