我喜欢这样做的方式是始终将配置作为 shared_ptr 携带到 const 对象。每当您需要配置时(请记住一致性;通常最好使用相同的配置处理整个请求,即使该配置在请求处理代码结束时在技术上已经过时),您会得到一个 shared_ptr 。后台工作任务可以在工作周期的方便点重置其 shared_ptr 的值,试图避免保持配置太久(因为它可能会过时)。
如果只有一项任务可以更改配置,则效果很好;它构造了一个全新的配置对象,然后重置它的 shared_ptr,[edit] 它用锁保护它,见下文[/edit]。一旦没有其他任务使用旧配置对象,旧配置对象就会消失。
一个细节:您不能传递指向配置对象部分的指针,除非您确定只要指针持续存在,您就会保留指向该配置对象的 shared_ptr。如果配置包括例如名称到子配置的映射,这可能会很烦人。
虽然共享指针有一些开销,但它可能比保持配置的整个副本同步要少(当然,除非配置很小,如果是这样,我们可能不会进行这种对话)。在大多数应用程序中,配置更改相对较少,因此在任何给定时间您都不太可能拥有两个以上的配置对象。您通常可以安排将 shared_ptr 创建为每个请求一个,因此 shared_ptr 同步是微不足道的。
YMMV,但我发现效果很好。
[编辑] 正如几位评论者所指出的,我应该明确说明锁定要求。配置更新程序保留一个主 shared_ptr,它受到读写锁的保护。它在更新指针时需要持有写锁。它还导出一个接口,该接口将 shared_ptr 返回到当前配置;该接口在持有读锁的同时复制 shared_ptr。由于配置更改很少且 shared_ptr 很小,因此锁争用非常少。 [1]
除了配置任务本身,没有人需要担心锁,因为其他 shared_ptr 不应该被多个任务共享:每个任务都应该有自己的。
[1] 在我写这篇文章的时候,我意识到我一直这样做的方式,包括在主 shared_ptr 上调用 .reset,如果配置的析构函数很慢(如果例如,配置包含大量的 std::string)。最好扩展 reset 的实现(这只是一个带有临时 NULL shared_ptr 的交换),将交换放在锁守卫内并让(临时的)析构函数解锁运行。但是,考虑到配置更改非常罕见(至少在我与之关联的任何服务器中),我怀疑它是否会产生任何明显的差异。