多线程应用中GetProcessMemoryInfo及MemoryTester代码是否线程安全?
多线程环境下这段代码的线程安全性分析
结论:这段代码并非线程安全,核心问题集中在静态成员变量s_memoryLimitInBytes的读写同步机制缺失上,具体问题如下:
1. 共享变量的读写存在原子性与可见性风险
静态变量s_memoryLimitInBytes被多线程共享,setMemoryLimitInBytes是写入操作,checkIfMemoryExceedsLimit、getMemoryLimitInBytes是读取操作:
- 虽然
size_t的单次读写在x86/x64等平台可能表现为原子操作,但C++标准不保证其原子性,在弱内存模型的平台上可能出现部分写入的异常情况。 - 更关键的是可见性问题:一个线程修改变量后,其他线程可能因CPU缓存机制,无法立即读取到最新值,导致基于过期数据的错误判断。
2. 复合操作存在竞态条件
checkIfMemoryExceedsLimit函数包含两次对s_memoryLimitInBytes的读取:
if (s_memoryLimitInBytes == 0) { // 第一次读取 return false; } return (getMemoryUseInBytes() > s_memoryLimitInBytes); // 第二次读取
两次读取之间,若有其他线程调用setMemoryLimitInBytes修改变量值,会直接破坏逻辑正确性:
- 比如线程A第一次读取到变量为0,准备返回false时,线程B将其设置为非0值,此时线程A本该执行内存检查却直接返回,失去了内存限制的作用。
- 反之,若第一次读取到非0值,但第二次读取前变量被改为0,会出现用内存使用量和0比较的错误判断。
3. 可安全使用的部分
getMemoryUseInBytes调用的Windows API(GetCurrentProcess、GetProcessMemoryInfo)本身是线程安全的,系统级API内部已处理多线程访问的同步逻辑,这部分不会引发线程安全问题。
修复建议
要实现线程安全,可通过两种方式处理:
- 使用C++标准库的
std::atomic<size_t>替换普通size_t,原子类型可保证读写的原子性与可见性。 - 若需保证
checkIfMemoryExceedsLimit这类复合操作的原子性,需添加互斥锁(如std::mutex),确保整个判断过程不被其他线程的写入操作打断。
原子类型修改示例
MemoryTester.h
#include <atomic> class MemoryTester { public: static bool checkIfMemoryExceedsLimit(); static void setMemoryLimitInBytes(size_t limit); static size_t getMemoryLimitInBytes(); static size_t getMemoryUseInBytes(); private: static std::atomic<size_t> s_memoryLimitInBytes; };
MemoryTester.cpp
#include "MemoryTester.h" #include "windows.h" #include "psapi.h" std::atomic<size_t> MemoryTester::s_memoryLimitInBytes = 0; bool MemoryTester::checkIfMemoryExceedsLimit() { size_t limit = s_memoryLimitInBytes.load(std::memory_order_acquire); if (limit == 0) { return false; } return (getMemoryUseInBytes() > limit); } void MemoryTester::setMemoryLimitInBytes(size_t limit) { s_memoryLimitInBytes.store(limit, std::memory_order_release); } size_t MemoryTester::getMemoryLimitInBytes() { return s_memoryLimitInBytes.load(std::memory_order_acquire); } size_t MemoryTester::getMemoryUseInBytes() { PROCESS_MEMORY_COUNTERS_EX pmc; GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); return pmc.PrivateUsage; }
内容的提问来源于stack exchange,提问作者SIMEL
相关产品推荐
相关产品推荐

