C++中全局变量的处理方法心得(单例、访问器、修改器)

一、概述

在进行实际项目开发时,我们经常会遇到需要控制程序流程的"开关"。这些开关可能在多个源文件中被频繁使用,因此全局变量成为了一种常见的解决方案。近期,我在项目中也采用了全局变量,但很快意识到在多线程环境下,这些全局变量可能会被频繁地修改,从而引发潜在的问题。尽管我目前负责的项目尚未涉及多线程编程,但这个问题显然是未来可能面临的挑战。

为了解决这一问题,我查阅了大量资料,并逐步完善了我的解决方案。以下是我的经验总结,希望能为遇到类似问题的开发者提供参考。

  1. 使用全局变量的考量:全局变量虽然方便,但在多线程环境中,它们可能会成为性能瓶颈和错误来源。因此,我们需要谨慎使用,并寻找更安全的替代方案。

  2. 多线程环境下的挑战:在多线程环境中,全局变量的读写操作需要同步,以避免竞态条件和数据不一致的问题。这通常需要使用锁机制,但锁的使用可能会降低程序的性能。

  3. 探索替代方案:为了避免全局变量带来的问题,我探索了一些替代方案,如使用局部变量、传递参数、设计模式等。这些方法可以减少对全局状态的依赖,提高程序的可维护性和可扩展性。

  4. 优化锁的使用:如果必须使用全局变量,那么合理使用锁是关键。我学习了不同的锁机制,如互斥锁(Mutex)、读写锁(RWLock)等,并根据具体情况选择合适的锁策略,以减少锁的竞争和提高程序的并发性能。

  5. 代码的可维护性:在设计程序时,我更加注重代码的可维护性。通过模块化设计和清晰的接口定义,我确保了代码的可读性和可维护性,这有助于减少未来可能出现的问题。

  6. 性能测试与优化:在实现解决方案后,我进行了性能测试,以确保新的设计不会对程序的性能产生负面影响。根据测试结果,我进一步优化了代码,以实现最佳的性能和稳定性。

二、具体代码实现

cpp 复制代码
/**
 * @class System
 * @brief 单例模式的系统类,提供全局访问和共享数据。
 */
class System
{

private:
    /**
     * @brief 私有化构造函数,防止外部实例化。
     */
    System() = default;

    Json::Value m_jReadValue;  /**< 用于存储读取的JSON值 */
    Json::Value m_jWriteValue; /**< 用于存储写入的JSON值 */

    std::atomic<bool> m_bCondition{false}; /**< 标志是否更新了条件 */
    std::atomic<bool> m_bWriteFlag{false};    /**< 标志是否需要写入数据 */

    std::mutex mutex_; /**< 用于保护 JSON 值的并发访问 */

public:
    /**
     * @brief 获取System类的唯一实例。
     * @return System的唯一实例引用。
     */
    static System &getInstance()
    {
        static System instance; // 局部静态变量,线程安全
        return instance;
    }

    // 删除拷贝构造函数和赋值操作符,确保只有一个实例
    System(const System &) = delete;
    System &operator=(const System &) = delete;

    // 读取的json
    Json::Value getReadValue()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return m_jReadValue;
    }

    void setReadValue(const Json::Value &value)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        m_jReadValue = value;
    }

    // 发送的json
    Json::Value getWriteValue()
    {
        std::lock_guard<std::mutex> lock(mutex_);
        return m_jWriteValue;
    }

    void setWriteValue(const Json::Value &value)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        m_jWriteValue = value;
    }

    // 获取和设置更新标志
    bool getConditionUpdated() const
    {
        return m_bCondition.load();
    }

    void setConditionUpdated(bool updated)
    {
        m_bCondition.store(updated);
    }

    // 获取和设置写入标志
    bool getWriteFlag() const
    {
        return m_bWriteFlag.load();
    }

    void setWriteFlag(bool flag)
    {
        m_bWriteFlag.store(flag);
    }

};

具体调用

cpp 复制代码
System &System = System::getInstance();

// 检查 DBM 条件
if (System.getConditionUpdated())
{

        // 根据写标志位执行相应的操作
        if (!dbmSystem.getWriteFlag())
        {
                // 执行读操作
        }
        else
        {
                // 执行写操作
        }

        // 重置条件更新标志位
        System.setConditionUpdated(false); // 使用 setter 重置 g_dbmConditionUpdated
}

解释

此代码主要是为了服务,从客户端传过来的读取与写入的JSON文件,与判断当前是写模式还是读模式的开关(全局变量)。

1、单例模式的使用 :为了保证程序中只存在一个全局共享的 System 实例,我们使用了单例模式。通过将构造函数私有化并提供静态的 getInstance 方法,确保在任何地方都可以安全、统一地访问同一个实例。这种模式避免了重复创建对象的开销,尤其是在需要维护全局状态的场景下。

2、线程安全的实现 :考虑到全局变量在多线程环境中可能被并发访问,我们使用了 std::mutexstd::atomic 来保护共享资源。

  • 对于复杂的对象(如 Json::Value ),使用 std::mutex 确保在多个线程中不会出现竞态条件。每次读取或修改这些共享数据时,我们使用 std::lock_guard 来自动管理锁的生命周期,确保锁的正确释放。
  • 对于简单的布尔标志(如 m_bConditionm_bWriteFlag ),我们使用了 std::atomic,以避免手动加锁的开销,并且保证在并发条件下的读写安全。

3、​删除**** ****拷贝构造和赋值操作符:通过删除拷贝构造函数和赋值操作符,确保类的唯一性,不允许创建类的副本,防止不必要的实例化带来额外的复杂性。

4、访问器和修改器的使用 :通过 getset 方法来访问或修改全局变量的值。这种方式不仅封装了内部实现细节,还通过 std::lock_guard 确保了线程安全的操作,避免了直接暴露全局变量带来的安全隐患。

这种设计方式不仅保持了全局状态的一致性,还避免了多线程环境下潜在的竞态条件,提升了代码的可维护性和可扩展性。

相关推荐
程序猿阿伟3 分钟前
《C++中的魔法:实现类似 Python 的装饰器模式》
java·c++·装饰器模式
Mr. zhihao5 分钟前
装饰器模式详解:动态扩展对象功能的优雅解决方案
java·开发语言·装饰器模式
zyhomepage5 分钟前
科技的成就(六十四)
开发语言·人工智能·科技·算法·内容运营
Ethan Wilson12 分钟前
C++/QT可用的websocket库
开发语言·c++·websocket
小宇1 小时前
The valid characters are defined in RFC 7230 and RFC 3986
java·开发语言·后端·tomcat
尘浮生1 小时前
Java项目实战II基于Spring Boot的美食烹饪互动平台的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·微信小程序·小程序·美食
杨荧1 小时前
【JAVA毕业设计】基于Vue和SpringBoot的校园美食分享平台
java·开发语言·前端·vue.js·spring boot·java-ee·美食
糊涂君-Q1 小时前
Python小白学习教程从入门到入坑------第十九课 异常模块与包【下】(语法基础)
开发语言·python·学习·程序人生·改行学it
ergevv1 小时前
类的变量的初始化:成员初始化列表、就地初始化
c++·初始化·
爱编程的小新☆1 小时前
Java篇图书管理系统
java·开发语言·学习