寻找一种无锁的方法来交换两个关联容器。

3
我在尝试实现特性切换模式。为此,我将包含特性切换的配置文件内容加载到一个映射表中。之后,有一个函数可以检查特性切换是否设置了。现在,我们的开发人员想要一个重新初始化映射表的函数,以便在应用程序运行时更改开关(编辑文件,调用重新初始化)。
由于切换机制应该很快,我希望实现最快的读取开关的方式。重新初始化映射表可能会很慢 - 这没问题。不写入只读取是线程安全的,但是在重新初始化函数中,我需要注意线程问题。
我的当前解决方案在读取函数中使用读锁,在写入函数中使用独占锁。但我的目标是在读取函数中不使用锁。有什么想法吗?
敬礼 托比亚斯

内在操作怎么样?不完全是锁定,但确保您可以在不同时更改值的情况下读取该值。 - Twifty
但是在我有一个复杂的数据结构中,你根本无法进行单个内部操作。我看到的一个选择是在重新初始化时故意使用内存泄漏。但是我想避免这种泄漏。 - Tobias Langner
不知道涉及的数据类型,这是一个难以回答的问题。如果只是简单的读取 int 或 boolean,则内部函数是最佳选择。对于更复杂的情况... 保留两份地图怎么样?一份用于写入,在某些间隔刷新更改到另一份上。仍需要锁定,但可以限制时间。 - Twifty
就像我所说的那样 - 这是由Fowler先生提出的功能切换模式(http://martinfowler.com/bliki/FeatureToggle.html)。因此,您需要一个关联容器,将字符串解析为布尔值。但仍然需要一个包含多个值的容器。 - Tobias Langner
1个回答

1
唯一不安全的操作应该是修改树的结构。仅仅更新节点中的值是安全的。因此,在“重新初始化”时,只需访问“旧”树中的每个节点,如果新配置没有设置,则将其保持不变或更改为“未知”状态。如果新配置对现有树中的节点有设置,则简单地更新并继续进行。
如果您真的需要能够随时添加新的功能切换(为什么?),则可以创建第二个数据结构,它只是一个愚蠢的、扁平的“添加”功能列表,并且只会附加到那里。然后,当读者查询特性时,首先查找树(快速),然后在列表中查找(慢,但不常见)。
最后,您可以像“z缓冲”一样做,其中您保留两个完整的树和指向“活动”树的指针。您只更新“非活动”树,然后翻转指针以使其活动。当读者正在使用活动树时,这可能需要一个简单的引用计数操作(因此您不会在旧旧树上更新第二次),但这应该很便宜。

问题在于,如果有人向树中添加新节点。Std::map 可能会重新平衡,然后我就会遇到问题。但是翻转的想法可能有效,因为我可以锁定重新初始化并保证没有人同时在非活动状态下工作。我看到的唯一问题是,如果在第一次调用重新初始化后不久再次调用,则现在仍处于非活动状态的树中可能仍然存在一些读取器,而它正在被清除/重新初始化。 - Tobias Langner
我同意所有这些担忧。我认为在我的回答中已经探讨过了。请仔细阅读,希望你能看到我建议这些问题可以安全地忽略、缓解或解决。 - John Zwinck
关于添加新功能开关的使用情况 - 我们与像Smalltalk这样的“交互式”开发环境一起工作。他们不想关闭IDE来引入新的开关。他们可以实时更改代码。 - Tobias Langner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接