当前位置: 首页 > ds >正文

防重入或并发调用(C++)

防重入或并发调用

  • 1. std::mutex + std::unique_lock(try_to_lock)
  • 2. QMutex + QMutexLocker(AdoptLock)
  • 3. std::atomic_flag + 简易 RAII 守卫
  • 4. 减少重复样板:宏或模版封装
    • 模板
  • 小结

通常我们要防止同一方法在执行过程被「重入」(Re-entrance)或被并发调用,可以在方法入口做一个简单的“忙碌”检测,常见做法有两种:基于互斥锁(mutex)/尝试锁(try-lock),或基于原子标志(atomic flag)+ 小型 RAII 守卫。

1. std::mutex + std::unique_lock(try_to_lock)

#include <mutex>class MyClass {
public:void foo() {// 尝试获取锁,如果获取失败说明已有一次 foo() 在执行,直接返回std::unique_lock<std::mutex> lk(foo_mtx_, std::try_to_lock);if (!lk) {// 可选:记录日志或发信号告知“已在执行中”return;}// —— 这里是真正的业务逻辑 ——  doHeavyWork();}void bar() {std::unique_lock<std::mutex> lk(bar_mtx_, std::try_to_lock);if (!lk) return;doOtherWork();}private:std::mutex foo_mtx_;std::mutex bar_mtx_;void doHeavyWork();void doOtherWork();
};
  • 优点:跨线程安全;不用显式解锁,unique_lock 会在作用域尾自动释放。

  • 缺点:每个方法要维护一个 std::mutex 成员,稍微繁琐。

2. QMutex + QMutexLocker(AdoptLock)

如果你想全部用 Qt 类型,也可以:

#include <QMutex>
#include <QMutexLocker>class MyClass {
public:void foo() {// tryLock() 返回 false 时表示已有执行if (!fooMtx_.tryLock()) return;// 采用 AdoptLock 模式,让 QMutexLocker 在析构时释放QMutexLocker locker(&fooMtx_, QMutexLocker::AdoptLock);doHeavyWork();}private:QMutex fooMtx_;void doHeavyWork();
};

tryLock() + QMutexLocker(…, AdoptLock) 等同于上面 std::try_to_lock 的模式。

3. std::atomic_flag + 简易 RAII 守卫

如果你仅仅需要同线程下防止重入,或者执行体非常短暂,也可以考虑更轻量的原子标志法:

#include <atomic>class MyClass {
public:void foo() {// test_and_set 返回前一个值:如果已经被 set 过(true),就直接 returnif (busyFoo_.test_and_set(std::memory_order_acquire)) return;// RAII 守卫:离开作用域时自动 clearstruct FlagGuard {std::atomic_flag &f;FlagGuard(std::atomic_flag &flag): f(flag) {}~FlagGuard() { f.clear(std::memory_order_release); }} guard(busyFoo_);doHeavyWork();}private:std::atomic_flag busyFoo_ = ATOMIC_FLAG_INIT;void doHeavyWork();
};
  • 优点:无锁开销(其实底层也是原子操作),代码简洁。

  • 缺点:只能防同线程或跨线程对同方法的并发调用,不适合复杂锁依赖。

4. 减少重复样板:宏或模版封装

如果方法很多,不想给每个都写一堆相似代码,可以把“尝试加锁 + RAII 解锁”封装成一个小宏或模版,例如:

// nonreentrant.h
#pragma once
#include <atomic>
#define NON_REENTRANT(method)                          \static std::atomic_flag _busy_##method = ATOMIC_FLAG_INIT; \if (_busy_##method.test_and_set(std::memory_order_acquire)) \return;                                         \auto _guard_##method = [&]() {                     \struct G { ~G() { _busy_##method.clear(std::memory_order_release); } } g; \}();                                                // MyClass.cpp
#include "nonreentrant.h"
void MyClass::foo() {NON_REENTRANT(foo);// … 实际逻辑 …
}
void MyClass::bar() {NON_REENTRANT(bar);// … 实际逻辑 …
}

这样每个方法顶部只要一行 NON_REENTRANT(方法名),就能自动完成标志检测和清理。

模板

// NonReentrantGuard.h
#pragma once
#include <atomic>/*** @brief 非重入守卫:同一 Tag 只允许一个实例“激活”其所在作用域** Tag 用来区分各个不同的方法/调用点。*/
template<typename Tag>
class NonReentrantGuard {
public:NonReentrantGuard(): engaged_(!flag_.test_and_set(std::memory_order_acquire)){}~NonReentrantGuard() {if (engaged_) {flag_.clear(std::memory_order_release);}}/// 转换为 bool:为 true 时表示“首次进入”,可以继续执行;为 false 则应直接返回explicit operator bool() const noexcept {return engaged_;}private:static std::atomic_flag flag_;bool engaged_;
};// 必须在某个翻译单元里定义静态成员
template<typename Tag>
std::atomic_flag NonReentrantGuard<Tag>::flag_ = ATOMIC_FLAG_INIT;
// MyClass.h
#include "NonReentrantGuard.h"class MyClass {
public:void foo();void bar();private:void doHeavyWork();void doOtherWork();
};

小结

  • 跨线程/性能可控:std::mutex + std::try_to_lock 或者 Qt 的 QMutex::tryLock()

  • 轻量级同线程:std::atomic_flag + 简易 RAII 守卫

  • 大批方法:可用宏或模板封装上面逻辑,减少重复代码

选用时只要注意解锁一定要在所有退出路径(包括异常)都会执行 —— RAII 守卫或 unique_lock/QMutexLocker 都能帮你做到这一点。这样就能保证「正在运行」的那次调用结束前,任何重复触发都会被忽略。

http://www.xdnf.cn/news/6059.html

相关文章:

  • C语言指针循环使用指南
  • Ansys 产品在Windows系统的卸载(2025R1版)
  • 【Redis】RedLock实现原理
  • 笔试强训(十七)
  • 12.1寸工业液晶屏M121XGV20-N10显示单元技术档案
  • 126.在 Vue 3 中使用 OpenLayers 实现绘制正方形、正三角形、正五边形
  • 使用PHP对接日本股票市场数据
  • 数据工具:数据同步工具、数据血缘工具全解析
  • Doris重建ROUTINE任务过程
  • vue3实现与不同的界面跳转【路由 vue-router】
  • WebGL入门:光照原理
  • binlog日志以及MySQL的数据同步
  • 项目三 - 任务5:清洗网址中垃圾字符
  • 电池自动点焊机:多领域电池制造的核心设备
  • UE5中制作动态数字Decal
  • ES6 语法
  • Rust 环境变量管理秘籍:从菜鸟到老鸟都爱的 dotenv 教程
  • Visual studio 打包方法
  • 计算机系统----软考中级软件设计师(自用学习笔记)
  • Biba安全模型详解:守护信息系统完整性的基石
  • 加速度策略思路
  • SwarmUI 基于.NET开发的开源AI图像生成WEB用户界面系统
  • git-gui界面汉化
  • 【3-2】HDLC
  • 详解注意力机制
  • Linux文件编程——读写结构体、链表等其他类型的数据
  • 9.9 Ollama私有化部署Mistral 7B全指南:命令行交互到API集成全流程解析
  • 格雷希尔G10和G15系列自动化快速密封连接器,适用于哪些管件的密封,以及它们相关的特性有哪些?
  • 参考UTD的上市公司供应链信息数据库(2017-2022)
  • 深度学习模型在目标检测任务中的前向传播(forward)和反向传播(backward)过程