Valgrind下C++回测程序移除cout后死锁冻结问题求助
问题分析与解决方案
核心问题根源
- 内存可见性缺失:全局变量
done是普通bool类型,未做同步处理。后台线程修改done = true后,main线程可能因CPU缓存优化无法感知到该更新,导致循环无法正常退出。 - 忙等引发线程饥饿:移除
cout语句后,main线程进入无延迟的忙等循环,在Valgrind的调度环境下,后台线程难以获得CPU时间片执行数据初始化任务,无法填充data_frames或更新done状态,最终表现为程序冻结。而cout的IO操作会引入天然延迟,让main线程暂停,给后台线程留出执行窗口。
具体修复步骤
1. 修复内存可见性问题
将done改为原子类型,确保跨线程的状态更新可见:
在dataparser.cpp中:
- 添加头文件
<atomic> - 将
bool done = false;替换为std::atomic<bool> done = false;
2. 避免忙等,给后台线程执行机会
在main函数的循环中,当没有数据可读取时,让出CPU资源:
修改main.cpp的while循环:
while (1) { auto tmp = getNextKLine(); if (tmp.has_value()) cout << idx++ << ": " << tmp.value().open_time << '\n'; else if (isDone()) break; std::this_thread::yield(); // 让出CPU,允许后台线程执行 }
或者使用短时间睡眠(更稳妥):
#include <chrono> // 需要添加这个头文件 // ... while (1) { auto tmp = getNextKLine(); if (tmp.has_value()) cout << idx++ << ": " << tmp.value().open_time << '\n'; else if (isDone()) break; std::this_thread::sleep_for(std::chrono::milliseconds(1)); }
3. 修复数据读取逻辑(可选但必要)
readTradesBetweenTime函数中有一条提前return ts;语句,导致后续的交易数据读取逻辑完全未执行,会导致AggTrade的trades字段始终为空。需要注释或删除这条不必要的return:
std::vector<Trade> readTradesBetweenTime(int64_t first_id, int64_t last_id) { static std::vector<Trade> trade_buffer; std::vector<Trade> ts; // return ts; // 删除或注释此行 // ... 后续逻辑保留 }
额外优化建议
- 避免使用
detach()管理后台线程,改用join()更安全,确保main线程等待后台线程完成后再退出。修改initParser函数:
void initParser(FILE *kl, FILE *t, FILE *a) { k = kl, tr = t, at = a; std::thread thr(readKLine); // thr.detach(); thr.join(); // 改为join,等待数据初始化完成后再返回 }
如果需要异步加载数据,可使用条件变量替代忙等,实现更高效的线程同步。
内容的提问来源于stack exchange,提问作者Le Xuan Ky
相关产品推荐
相关产品推荐

