【问题标题】:Does a cache need to synchronized?缓存需要同步吗?
【发布时间】:2009-01-30 20:14:47
【问题描述】:

这似乎是一个幼稚的问题,但我与一位同事进行了讨论,我认为没有真正需要缓存是线程安全/同步的,因为我认为这无关紧要谁在输入一个值,因为给定键的值应该是“恒定的”(因为它最终来自同一来源)。如果值可以很容易地更改,那么缓存本身似乎并不是很有用(因为如果您关心该值“当前正确”,您应该转到原始源)。

我认为至少使 GET 同步的主要原因是,如果在缓存中丢失非常昂贵,并且您不希望每个线程都出去获取一个值以放回缓存中。即使这样,您也需要在读取-读取-放置周期中真正阻止所有消费者的东西。

无论如何,我的工作假设是散列本质上是线程安全的,因为对于任何 {key,value} 组合,该值要么为空,要么与谁“先”到那里无关紧要写。

问题是:这是一个合理的假设吗?

更新:我的问题的真正范围是非常简单的 id->value 样式缓存(或 {parameters}->{calculated value} 无论谁写入缓存,值都是相同的,我们是只是试图从“重新计算”/返回数据库中保存。对象的实际图形不相关,缓存通常是长期存在的。

【问题讨论】:

    标签: c# java multithreading caching


    【解决方案1】:

    对于哈希的大多数实现,您需要同步。如果哈希表需要扩展/重新散列怎么办?如果两个线程试图在键不同的哈希表中添加一些东西,但哈希冲突怎么办?他们都可以同时以不同的方式修改哈希表中的同一个槽。假设您正在使用哈希表来实现缓存(您在问题中暗示了这一点),如果您还不熟悉的话,我建议您阅读一些关于 hash tables are implemented 的详细信息。

    【讨论】:

    • 非常好的点......当重新生成存储桶时,它将使用它所认为的当前键/值集,如果两个 rehash() 调用发生冲突,结果可能有趣。谢谢!
    【解决方案2】:

    写入并不总是原子的。您必须要么使用原子数据类型,要么提供一些同步(RCU、锁等)。没有共享数据本身是线程安全的。或者通过坚持无锁算法(即在可能和可行的情况下)来消除这种情况。

    【讨论】:

      【解决方案3】:

      只要获取和释放锁的成本低于重新创建对象(从文件或数据库或其他)的成本,所有对缓存的访问确实应该同步。如果不是,您根本不需要缓存。 :)

      【讨论】:

        【解决方案4】:

        如果您想避免数据损坏,您必须进行同步。当缓存包含多个必须以原子方式更新的表时尤其如此。想象一下,您有一个 DMV(机动车辆部门)的数据库。您将一个新人添加到数据库中,该人将拥有自动注册记录以及收到的票证记录,以记录家庭住址和可能的其他联系信息。如果您不以原子方式更新这些表(在数据库中在缓存中),那么任何从缓存中提取数据的客户端都可能会得到不一致的数据。

        是的,任何一条数据都可能是不变的,但数据库通常保存的数据——如果不一起自动更新——可能会导致数据库客户端得到不正确、不完整或不一致的结果。

        【讨论】:

        • 我或许应该更多地考虑我的问题。我指的是简单对象的缓存缓存(id->value)。如果担心对象的“脏度”,那么我正在考虑的缓存(LRU 长寿命缓存,它没有从源获取更新)将不合适。
        【解决方案5】:

        如果您使用的是 Java 5 或更高版本,则可以使用 ConcurrentHashMap。这以线程安全的方式支持多个读取器和写入器。

        【讨论】:

        • 如果缓存包含多个需要原子更新的表,这还不够。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-05-16
        • 1970-01-01
        • 1970-01-01
        • 2012-08-25
        • 2016-09-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多