offline tmp

dev_offline
liusiyang 3 weeks ago
parent 546ce62fe6
commit 44ce8cc61c

@ -26,7 +26,7 @@
// resize 图片的 宽度 // resize 图片的 宽度
#define RESIZE_IMAGE_WIDTH 1680 #define RESIZE_IMAGE_WIDTH 1680
// #define AI_Time #define AI_Time
enum TEM_IMG_IDX_ enum TEM_IMG_IDX_
{ {

@ -153,6 +153,8 @@ public:
void AddLog(std::string str); void AddLog(std::string str);
// 添加图片信息 // 添加图片信息
int AddDetImage(std::shared_ptr<shareImage> p); int AddDetImage(std::shared_ptr<shareImage> p);
// ✅ 处理完成后释放中间数据,减少内存占用
void ReleaseIntermediateData();
DetStep getStep(); DetStep getStep();
void setStep(DetStep step); void setStep(DetStep step);
bool IsNotDet(); bool IsNotDet();

@ -18,6 +18,49 @@ ImageAllResult::~ImageAllResult()
{ {
} }
// ✅ 处理完成后释放中间 cv::Mat 数据,减少内存占用
// 注意CheckResultresult中的图像数据必须保留
void ImageAllResult::ReleaseIntermediateData()
{
std::lock_guard<std::mutex> lock_cam(mtx_Det);
// ✅ 释放原始输入图像(处理完成后不再需要)
if (result && result->in_shareImage)
{
if (!result->in_shareImage->img.empty())
{
result->in_shareImage->img.release();
}
if (!result->in_shareImage->AI_maskImg.empty())
{
result->in_shareImage->AI_maskImg.release();
}
}
// ✅ AI_Time 开启时需要保留 detImg用于生成缺陷小图和 AI_Qx_MaskList用于 AI 调试图像)
// 其他中间数据可以安全释放
#ifndef AI_Time
if (!detImg.empty()) { detImg.release(); }
#endif
if (!AI_detImg.empty()) { AI_detImg.release(); }
if (!AIMaskImg.empty()) { AIMaskImg.release(); }
if (!AI_127CellMaskImg.empty()) { AI_127CellMaskImg.release(); }
if (!resultImg.empty()) { resultImg.release(); }
if (!shieldImg.empty()) { shieldImg.release(); }
if (!AI_shieldImg.empty()) { AI_shieldImg.release(); }
if (!YX_MaskImg.empty()) { YX_MaskImg.release(); }
if (!LcakPol_MaskImg.empty()) { LcakPol_MaskImg.release(); }
// 释放 AI 检测中间结果
if (qx_DetAIResult) { qx_DetAIResult->Init(); }
if (cell127_DetAIResult) { cell127_DetAIResult->Init(); }
// ✅ AI_Time 开启时需要保留 AI_Qx_MaskListGetAIDetImg 使用)
#ifndef AI_Time
AI_Qx_MaskList.clear();
#endif
}
void ImageAllResult::AddLog(std::string str) void ImageAllResult::AddLog(std::string str)
{ {
LogList.push_back(str); LogList.push_back(str);

@ -666,6 +666,8 @@ int ImgCheckAnalysisy::Run(int nId)
CheckRun(); CheckRun();
m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE; m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE;
m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet); m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet);
// ✅ 处理完成后立即释放 ImageAllResult 的中间数据,减少内存占用
m_pImageAllResult->ReleaseIntermediateData();
m_nRun_Status = CHECK_THREAD_STATUS_IDLE; m_nRun_Status = CHECK_THREAD_STATUS_IDLE;
// printf("*--------%d\n", m_nRun_Status); // printf("*--------%d\n", m_nRun_Status);
} }

@ -0,0 +1,50 @@
#ifndef _MEM_MONITOR_H_
#define _MEM_MONITOR_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
// 获取当前进程 RSS (物理内存) 使用量,单位 MB
static inline long getRSS_KB()
{
long rss = 0;
FILE *fp = fopen("/proc/self/status", "r");
if (!fp) return -1;
char line[256];
while (fgets(line, sizeof(line), fp))
{
if (strncmp(line, "VmRSS:", 6) == 0)
{
// 格式: "VmRSS: 12345 kB"
const char *p = line + 6;
while (*p == ' ' || *p == '\t') p++;
rss = atol(p);
break;
}
}
fclose(fp);
return rss;
}
// 获取当前进程内存使用,打印格式化日志
static inline void printMemUsage(const char *tag, const char *extra)
{
long rss = getRSS_KB();
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
printf("[MEM] %-30s | VmRSS: %6ld MB | maxRSS: %6ld MB | %s\n",
tag, rss / 1024, usage.ru_maxrss / 1024, extra ? extra : "");
}
// 获取产品检测 pipeline 队列深度信息快照
#define MEM_LOG(tag, fmt, ...) \
do { \
long _rss = getRSS_KB(); \
printf("[MEM] %-30s | VmRSS: %6ld MB | " fmt "\n", tag, _rss / 1024, ##__VA_ARGS__); \
} while(0)
#endif // _MEM_MONITOR_H_

@ -3,6 +3,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "MemMonitor.h"
#include <unistd.h> #include <unistd.h>
#include <string> #include <string>
#include "CheckUtil.hpp" #include "CheckUtil.hpp"
@ -900,6 +901,7 @@ void deal::GetDealResultToQueu()
mutex_Result_list.lock(); mutex_Result_list.lock();
m_Result_list.push(checkResult); m_Result_list.push(checkResult);
mutex_Result_list.unlock(); mutex_Result_list.unlock();
m_nTotalResultsDrained.fetch_add(1); // ✅ 更新计数器
} }
else else
{ {
@ -1341,11 +1343,65 @@ int deal::CheckFileImg()
products[i].print("产品" + std::to_string(i + 1)); products[i].print("产品" + std::to_string(i + 1));
} }
// getchar(); // getchar();
printMemUsage("CheckFileImg-开始处理", "");
for (size_t i = 0; i < products.size(); i++) for (size_t i = 0; i < products.size(); i++)
{ {
char buf[128];
snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理前", i+1, products.size(), products[i].product_id.c_str());
printMemUsage(buf, "");
CheckProduct(products[i]); // 处理单个产品 CheckProduct(products[i]); // 处理单个产品
snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理后", i+1, products.size(), products[i].product_id.c_str());
printMemUsage(buf, "");
}
// ✅ 修复:所有产品处理完成后,通知后台线程退出
printf("\n>>> 所有产品处理完成,正在停止后台线程...\n");
// 1. 通知产品读图线程退出
m_bProductReadExit = true;
m_productTaskCV.notify_all();
m_checkNotifyCV.notify_all();
// 2. 通知结果处理线程退出
m_bExit = true;
cv.notify_all();
// 3. 等待产品读图线程结束
for (auto &thread : m_productReadThreads)
{
if (thread && thread->joinable())
{
thread->join();
}
}
m_productReadThreads.clear();
// 4. 等待结果处理线程结束
for (auto &thread : ptr_ResultthreadList)
{
if (thread && thread->joinable())
{
thread->join();
}
}
ptr_ResultthreadList.clear();
// 5. 等待 GetResult 线程结束
if (ptr_GetResultthread && ptr_GetResultthread->joinable())
{
ptr_GetResultthread->join();
} }
ptr_GetResultthread.reset();
// 6. 等待 DealImg 线程结束(如果存在)
if (ptr_DealImgthread && ptr_DealImgthread->joinable())
{
ptr_DealImgthread->join();
}
ptr_DealImgthread.reset();
printf(">>> 所有后台线程已停止,程序退出.\n");
printf("\n"); printf("\n");
// TODO: 后续实现具体功能 // TODO: 后续实现具体功能
@ -1386,6 +1442,11 @@ int deal::CheckProduct(const LoadProductImages &product)
m_ProductFailedList.clear(); m_ProductFailedList.clear();
m_nCheckNotifiedCount = 0; m_nCheckNotifiedCount = 0;
// ✅ 修复内存泄漏清理上一个产品累积的原始图像数据cv::Mat
// 注意m_DetResult 是统计结果(轻量字符串/数字),不清除,跨产品累积
m_ImageInfoList.clear();
m_productIdList.clear();
// 3. 打印产品信息 // 3. 打印产品信息
printf("\n"); printf("\n");
printf("╔══════════════════════════════════════════════════════════\n"); printf("╔══════════════════════════════════════════════════════════\n");
@ -1437,6 +1498,53 @@ int deal::CheckProduct(const LoadProductImages &product)
SendCheckImg(info, IN_IMG_Status_End, true); SendCheckImg(info, IN_IMG_Status_End, true);
printf("\n"); printf("\n");
// ✅ 流控:等待 Run 线程处理完当前产品 + 结果全部保存完毕,再继续下一个产品
// 两步确认:
// 第1步 - 等 GetResultThread 把本产品所有结果从 m_CheckResultList 取出
// 第2步 - 等 ResultThread 把 m_Result_list 中的结果全部保存、释放内存
{
int startDrained = m_nTotalResultsDrained.load();
int expectedResults = (int)totalImgNum;
int maxWaitMs = 600000; // 最多等待10分钟
int waitedMs = 0;
// 第1步等结果被消费
printf("[流控-1] 等待产品 %s 的 %d 个检测结果被消费...\n", product.product_id.c_str(), expectedResults);
while (m_nTotalResultsDrained.load() - startDrained < expectedResults && waitedMs < maxWaitMs)
{
usleep(100000);
waitedMs += 100;
}
printf("[流控-1] 已完成,已消费 %d 个结果\n", m_nTotalResultsDrained.load() - startDrained);
// 第2步等结果全部保存完毕m_Result_list 排空)
printf("[流控-2] 等待结果保存完毕、内存释放...\n");
waitedMs = 0;
while (waitedMs < maxWaitMs)
{
bool queueEmpty;
{
std::lock_guard<std::mutex> lk(mutex_Result_list);
queueEmpty = m_Result_list.empty();
}
if (queueEmpty)
{
// 再等一小会确保最后一批 I/O 完成
usleep(500000);
break;
}
usleep(100000);
waitedMs += 100;
if (waitedMs % 10000 == 0)
{
size_t sz;
{ std::lock_guard<std::mutex> lk(mutex_Result_list); sz = m_Result_list.size(); }
printf("[流控-2] 等待中... 结果队列剩余 %zu 个\n", sz);
}
}
printf("[流控] 产品 %s 完全处理完毕,内存已释放,继续下一个产品\n\n", product.product_id.c_str());
}
// 6. 打印统计(不清理线程,继续下一个产品) // 6. 打印统计(不清理线程,继续下一个产品)
PrintProductStatistics(product); PrintProductStatistics(product);
// getchar(); // getchar();

@ -799,6 +799,7 @@ public:
std::mutex m_checkNotifyMutex; // 通知队列锁 std::mutex m_checkNotifyMutex; // 通知队列锁
std::condition_variable m_checkNotifyCV; // 通知条件变量 std::condition_variable m_checkNotifyCV; // 通知条件变量
std::atomic<int> m_nCheckNotifiedCount{0}; // 已通知数量 std::atomic<int> m_nCheckNotifiedCount{0}; // 已通知数量
std::atomic<int> m_nTotalResultsDrained{0}; // ✅ 已被消费的检测结果总数
std::set<std::string> m_productIdList; // 产品ID列表 std::set<std::string> m_productIdList; // 产品ID列表
}; };

@ -245,10 +245,5 @@ int main(int argc, char *argv[])
signal(SIGINT, handler); signal(SIGINT, handler);
test.start(); test.start();
while (true)
{
usleep(10 * 1000);
}
return 0; return 0;
} }

Loading…
Cancel
Save