无锁重新加载和共享常量对象



我的应用程序的配置是一个与多个线程共享的 const 对象。配置存储在集中位置,任何线程都可以访问它。我尝试构建一个无锁实现,它允许我加载新配置,同时仍然允许其他线程读取最后一个已知配置。

我当前的实现在更新shared_ptr和从中读取之间进行了竞赛。

template<typename T>
class ConfigurationHolder
{
public:
    typedef std::shared_ptr<T> SPtr;
    typedef std::shared_ptr<const T> CSPtr;
    ConfigurationHolder() : m_active(new T()) {}
    CSPtr get() const { return m_active; } // RACE - read
    template<typename Reloader>
    bool reload(Reloader reloader)
    {
        SPtr tmp(new T());
        if (!tmp)
            return false;
        if (!reloader(tmp))
            return false;
        m_active=tmp; // RACE - write
        return true;
    }
private:
    CSPtr m_active;
};

我可以为有问题的shared_ptr读/写访问添加一个shared_mutex,但我正在寻找一种可以保持实现无锁的解决方案。

编辑:我的GCC版本不支持atomic_exchange shared_ptr

EDIT2:需求说明:我有多个阅读器,可能有多个重装器(尽管这种情况不太常见(。读者需要持有一个配置对象,并且在他们阅读它时它不会改变。当最后一个读取器完成旧配置对象处理完毕时,必须释放它们。

您应该只更新编译器以获取原子共享指针操作。

如果做不到这一点,请将其包裹在shared_timed_mutex中。 然后测试它花了你多少钱。

正确编写自己的无锁共享指针系统相比,这两种方法都比工作量更少。

如果您必须:


这是一个黑客,但它可能会起作用。 它是指针本身的读取-复制-更新样式。

有一个std::vector<std::unique_ptr<std::shared_ptr<T>>>. 有一个std::atomic<std::shared_ptr<T> const*>"当前"指针和一个std::atomic<std::size_t> active_readers

vector存储您仍然活着的shared_ptr。 当您想更改时,请在背面推一个新的。 保留此shared_ptr的副本。

现在将"当前"指针换成新指针。 忙碌——等到active_readers达到零,或者直到你感到无聊。

如果active_readers达到零,请过滤vector以查找使用计数为 1 的 shared_ptr 秒。 将它们从vector中删除。

无论如何,现在将多余的shared_ptr放到您创建的状态。 并完成了写作。

如果需要多个编写器,请使用单独的互斥锁锁定此进程。

在读卡器端,递增active_readers 。 现在原子加载"当前"指针,制作指向shared_ptr的本地副本,然后递减active_readers

但是,我只是写了这个。 所以它可能包含错误。 证明并行设计的正确性是很困难的。

到目前为止,使其可靠的最简单方法是升级编译器并在shared_ptr上获取原子操作。


这可能过于复杂,我认为我们可以对其进行设置,以便在最后一个阅读器离开时清理T,但我的目标是正确性而不是回收T的效率。


通过读取器

上的最小同步,您可以使用条件变量来表示读取器已完成给定T;但这涉及与编写器线程的少量争用。


实际上,无锁算法

通常比基于锁的算法慢,因为互斥锁的开销并不像你担心的那么高。

包装shared_ptrshared_timed_mutex,编写者只是覆盖变量,这将是非常快的。 现有的读者将保持他们的旧shared_ptr很好。

相关内容

  • 没有找到相关文章

最新更新