两个数据库互锁,用全局变量互锁会偶发软件卡死

线程1:调用函数的部分代码

theApp.m_cmux = 1;

p->pView->PostMessage(WM_REFRESHSAMPLE, RF_SAMPLE, LPARAM§);

while (theApp.m_cmux)

{

Sleep(20);

}

theApp.m_cmux = 1;

p->pView->PostMessage(WM_REFRESHSAMPLE, RF_SAMPLE, LPARAM§);

其中PostMessage用于发送消息执行数据库写数据,写完数据,最后theApp.m_cmux = 0;

具体实现函数是:

int CSampleTrans::refreshRst(void)

{

CString m_strID;

if (refreshID)

{

cout << "更新结果:开始写入数据库 " << endl;

try

{}

theApp.m_cmux = 0;

cout << "更新结果:写入数据库完成1 " << endl;

return 1;

}

程序还有一个函数,线程2,其部分实现如下:

UINT CSampleTrans::XSYThread(LPVOID pParam)

{

CSampleTrans* p = (CSampleTrans*)pParam;

CString strr, str;

while (theApp.m_cmux)

{

Sleep(20);

}

p->pView->PostMessage(WM_REFRESHSAMPLE, AD_Sample, LPARAM§);//添加样本

}

PostMessage也是通过发送消息操作数据库,具体实现函数是:

long CCVF::addNewPt(CSampleProc *psp, int codeNum)

{

cout << "添加样本:开始写入数据库 " << endl;

long mainID(0);

while (theApp.m_cmux)

{

Sleep(20);

}

theApp.m_cmux = 1;//和检测完成信号互斥

// long m_rID = 0;

CString m_zkIDstr, m_IDstr;

CString str1, str2;

try

{}

theApp.m_cmux = 0;

cout << "添加样本:写入数据库完成 " << endl;

return mainID;

}

程序漏发连续执行150多样本时,卡死在addNewPt函数的 while (theApp.m_cmux)

{

Sleep(20);

}

以下是ai给的解释

原子变量(std::atomic)通俗解释

原子变量是C++11及以上标准提供的线程安全的基础数据类型 ,核心含义是:对该变量的「读取、修改、写入」等所有操作,都会被封装成「不可被中断的单步底层指令」------简单说,一个线程对原子变量执行操作时,要么完整执行完整个操作,要么完全没执行,中间不会被其他线程打断,从根本上避免多线程操作同一变量时的指令交错问题。

可以用一个通俗比喻理解:

  • 普通全局变量(如原代码的int m_cmux:操作像"拆分成三步做事"(比如先看变量值→再修改→最后写回),多线程同时操作时,CPU可能在"三步中间"切换线程,导致操作混乱(这就是你之前卡死的根源);
  • 原子变量(std::atomic<int> m_cmux:操作像"一步做完一件事",CPU一旦开始执行对它的操作,就会一口气完成,中途不会切到其他线程,多线程操作时永远不会出现"半截操作"的情况。

结合你的代码:原子变量解决了什么核心问题

你之前程序卡死的根源,是普通int m_cmux非原子操作特性 ------对m_cmux的赋值(=1/=0)、判断(while(m_cmux)),底层会拆分成「读取变量值→执行逻辑→写回新值」多步指令,高频率操作时线程切换会导致这些指令交错,最终让m_cmux被永久置1。

而改成std::atomic<int> m_cmux后,你代码中所有对m_cmux的操作都会变成原子操作,举两个核心场景:

1. 赋值操作:theApp.m_cmux = 1; / theApp.m_cmux = 0;
  • 普通int:底层分3步(读当前m_cmux值→计算新值1/0→写回内存),中途可能被其他线程打断;
  • 原子变量:3步合并为1步不可中断指令 ,要么成功把m_cmux改成1/0,要么没改,不会出现"改到一半被切线程"的情况。
2. 判断操作:while (theApp.m_cmux)
  • 普通int:每次循环都会"读取m_cmux值→判断是否非0",两步操作中间可能被其他线程修改m_cmux,导致判断结果失真;
  • 原子变量:"读取+判断"的核心读取操作是原子的,读取到的m_cmux值一定是某个线程操作完成后的真实值,不会读到"半截修改"的无效值。

原子变量的3个关键特性(贴合你的使用场景,无冗余)

  1. 语法完全兼容普通变量 :你原有代码中所有对m_cmux的操作(=1/=0/while判断)无需做任何修改,直接复用即可,这也是为什么说"最小改动解决卡死问题";
  2. 无需额外加锁 :原子变量的线程安全是底层硬件指令级别的保障,不需要手动加临界区、互斥量,比传统"全局变量+锁"的方式更轻量、效率更高;
  3. 仅保证自身操作原子性 :原子变量只确保对自身 的单个操作不可中断,若需要多个操作的原子性(比如"判断m_cmux=0后立即置1"),仍需配合锁,但你的代码中无此场景,直接用原子变量即可满足需求。

为什么原子变量能彻底解决你之前的卡死问题

你之前的卡死,是高频率操作下普通m_cmux的非原子指令交错 导致的致命时序:UI线程执行m_cmux=1后被临时挂起,后续的m_cmux=0没机会执行,最终m_cmux永久置1。

改成原子变量后,m_cmux=1的赋值是一步不可中断 的,且所有线程对m_cmux的读取都是真实值:

  • 要么UI线程完整执行m_cmux=1,后续只要执行到写库完成的逻辑,就一定会完整执行m_cmux=0
  • 要么UI线程没执行m_cmux=1,其他线程读到的m_cmux始终是0,不会出现"半截赋值导致永久置1"的情况;
    从根源上杜绝了m_cmux被永久置1的可能,自然也就不会出现addNewPt循环无限等待、UI线程阻塞卡死的问题。

简单总结

  1. 原子变量的核心:单个操作不可被中断,多线程操作时无"半截操作",保证变量值的真实、一致;
  2. 对你的代码:完全兼容原有逻辑,1行代码修改,就能解决普通变量非原子性导致的指令交错、值失真、永久置1等问题;
  3. 实际效果:连续执行150+甚至更多样本时,无论线程切换多频繁,对m_cmux的操作都不会混乱,程序不会再因该问题卡死。
相关推荐
端平入洛12 小时前
auto有时不auto
c++
爱可生开源社区14 小时前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸17718 小时前
《从零搭建NestJS项目》
数据库·typescript
郑州光合科技余经理1 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1231 天前
matlab画图工具
开发语言·matlab
加号31 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏1 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
dustcell.1 天前
haproxy七层代理
java·开发语言·前端
norlan_jame1 天前
C-PHY与D-PHY差异
c语言·开发语言
李慕婉学姐1 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端