【发布时间】:2013-11-12 16:31:23
【问题描述】:
我正在编写一些设置类,可以从我的多线程应用程序中的任何地方访问。我会经常阅读这些设置(因此读取访问应该很快),但它们不会经常写入。
对于原始数据类型,它看起来像 boost::atomic 提供了我需要的东西,所以我想出了这样的东西:
class UInt16Setting
{
private:
boost::atomic<uint16_t> _Value;
public:
uint16_t getValue() const { return _Value.load(boost::memory_order_relaxed); }
void setValue(uint16_t value) { _Value.store(value, boost::memory_order_relaxed); }
};
问题 1: 我不确定内存顺序。我认为在我的应用程序中我并不真正关心内存排序(是吗?)。我只是想确保getValue() 始终返回一个未损坏的值(旧值或新值)。那么我的内存排序设置是否正确?
问题 2: 这种使用boost::atomic 的方法是否推荐用于这种同步?还是有其他结构可以提供更好的读取性能?
我的应用程序中还需要一些更复杂的设置类型,例如std::string 或boost::asio::ip::tcp::endpoints 的列表。我认为所有这些设置值都是不可变的。因此,一旦我使用setValue() 设置值,值本身(std::string 或端点列表本身)就不再改变。因此,我再次只想确保获得旧值或新值,而不是某些损坏的状态。
问题 3: 这种方法是否适用于 boost::atomic<std::string>?如果没有,有什么替代方案?
问题 4: 更复杂的设置类型(如端点列表)如何?你会推荐像boost::atomic<boost::shared_ptr<std::vector<boost::asio::ip::tcp::endpoint>>> 这样的东西吗?如果没有,什么会更好?
【问题讨论】:
-
重新检查允许多个线程在需要时读取和写入这些设置的要求。线程调用 getBlahSetting() 并获得一个结果,然后在同一函数中再次调用它并获得 不同的结果,这是否有意义?我建议你设计一个硬同步点。
-
设置只能代表用户进行更改,并且用户很少可以同时做超过一件事。就目前而言,作家不应该超过一位。因此,我将在没有任何同步的情况下访问(=读取)配置(因为它经常发生)。从修改线程中制作一个深拷贝,以应对配置被更改的罕见情况(同样,这不需要同步)。然后将单个
seq_cst存储到指向每个人都使用的配置对象的指针(这很昂贵,但您可以负担得起,因为它很少发生),然后就完成了。这可能需要... -
... 在其他线程获取更改之前的片刻,但这通常不是问题。他们最终会的。如果这有问题,你别无选择,只能使用锁(大概是读写器锁),然后性能会很糟糕。或者,线程可以保留配置状态的本地副本(或共享引用计数的副本),并在明确定义的时间通过任务队列明确通知更新配置。这消除了可能的种族。
-
问题是,所有这些原子操作不太可能对您有很大帮助,例如,如果您以原子方式修改一些字符串和 TCP 端点,然后以原子方式修改另一个线程(或其中两个)使用错误的 TCP 端点等获取新的字符串值。如果配置的整体是荒谬的,那么是否每个都自动更新无关紧要。当然,除非这对你来说无关紧要。唯一合理安全的方法是使用读写器锁,或在定义的时间显式获取新配置(如通过指向完整的新配置集的新指针的“快照”)。
-
@Damon 我相信这(实际上是这些)应该是一个答案。
标签: c++ multithreading boost synchronization atomic