一、概述
在进行实际项目开发时,我们经常会遇到需要控制程序流程的"开关"。这些开关可能在多个源文件中被频繁使用,因此全局变量成为了一种常见的解决方案。近期,我在项目中也采用了全局变量,但很快意识到在多线程环境下,这些全局变量可能会被频繁地修改,从而引发潜在的问题。尽管我目前负责的项目尚未涉及多线程编程,但这个问题显然是未来可能面临的挑战。
为了解决这一问题,我查阅了大量资料,并逐步完善了我的解决方案。以下是我的经验总结,希望能为遇到类似问题的开发者提供参考。
使用全局变量的考量:全局变量虽然方便,但在多线程环境中,它们可能会成为性能瓶颈和错误来源。因此,我们需要谨慎使用,并寻找更安全的替代方案。
多线程环境下的挑战:在多线程环境中,全局变量的读写操作需要同步,以避免竞态条件和数据不一致的问题。这通常需要使用锁机制,但锁的使用可能会降低程序的性能。
探索替代方案:为了避免全局变量带来的问题,我探索了一些替代方案,如使用局部变量、传递参数、设计模式等。这些方法可以减少对全局状态的依赖,提高程序的可维护性和可扩展性。
优化锁的使用:如果必须使用全局变量,那么合理使用锁是关键。我学习了不同的锁机制,如互斥锁(Mutex)、读写锁(RWLock)等,并根据具体情况选择合适的锁策略,以减少锁的竞争和提高程序的并发性能。
代码的可维护性:在设计程序时,我更加注重代码的可维护性。通过模块化设计和清晰的接口定义,我确保了代码的可读性和可维护性,这有助于减少未来可能出现的问题。
性能测试与优化:在实现解决方案后,我进行了性能测试,以确保新的设计不会对程序的性能产生负面影响。根据测试结果,我进一步优化了代码,以实现最佳的性能和稳定性。
二、具体代码实现
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::mutex
和 std::atomic
来保护共享资源。
- 对于复杂的对象(如
Json::Value
),使用std::mutex
确保在多个线程中不会出现竞态条件。每次读取或修改这些共享数据时,我们使用std::lock_guard
来自动管理锁的生命周期,确保锁的正确释放。- 对于简单的布尔标志(如
m_bCondition
和m_bWriteFlag
),我们使用了std::atomic
,以避免手动加锁的开销,并且保证在并发条件下的读写安全。
3、删除**** ****拷贝构造和赋值操作符:通过删除拷贝构造函数和赋值操作符,确保类的唯一性,不允许创建类的副本,防止不必要的实例化带来额外的复杂性。
4、访问器和修改器的使用 :通过 get
和 set
方法来访问或修改全局变量的值。这种方式不仅封装了内部实现细节,还通过 std::lock_guard
确保了线程安全的操作,避免了直接暴露全局变量带来的安全隐患。
这种设计方式不仅保持了全局状态的一致性,还避免了多线程环境下潜在的竞态条件,提升了代码的可维护性和可扩展性。