下面的代码应该缓存最后一次读取。LastValueCache是一个可以被许多线程访问的缓存(这就是我使用共享内存的原因)。对我来说,有竞争条件是可以的,但我希望其他线程能够看到修改后的LastValueCache。
class Repository
{
public Item LastValueCache
{
get
{
Thread.MemoryBarrier();
SomeType result = field;
Thread.MemoryBarrier();
return result;
}
set
{
Thread.MemoryBarrier();
field = value;
Thread.MemoryBarrier();
}
}
public void Save(Item item)
{
SaveToDatabase(item);
Item cached = LastValueCache;
if (cached == null || item.Stamp > cached.Stamp)
{
LastValueCache = item;
}
}
public void Remove(Timestamp stamp)
{
RemoveFromDatabase(item);
Item cached = LastValueCache;
if (cached != null && cached.Stamp == item.Stamp)
{
LastValueCache = null;
}
}
public Item Get(Timestamp stamp)
{
Item cached = LastValueCache;
if (cached != null && cached.Stamp == stamp)
{
return cached;
}
return GetFromDatabase(stamp);
}
}Repository对象被许多线程使用。我不想使用锁定,因为它会影响性能,在我的情况下,这比数据一致性更重要。问题是,,适合我需要的最小的同步机制是什么?,也许volatile,或者get和set中的单一MemoryBarrier就足够了?
发布于 2014-03-18 00:25:10
如果这很蠢,你就不用投我一票。
告诉我我就删掉。
但我没有遵循这个逻辑。
public void Save(Item item)
{
SaveToDatabase(item);
Item cached = LastValueCache;
if (cached == null || item.Stamp > cached.Stamp)
{
LastValueCache = item;
}
}您担心内存毫秒,但在更新缓存之前,您正在等待对数据库的写入。
基于公共物品的盖章是一个关键。
假设db写为20 ms。
db读为10 ms。
缓存获取和缓存集分别为2ms。
公众无效保存(项目)
SaveToDatabase(项目);20 ms
项缓存= LastValueCache;2ms
如果(缓存的== null _ item.Stamp > cached.Stamp) 1毫秒
LastValueCache =项目;2 ms
在LastValueCache = Item之前的23 ms期间,任何对公共项目Get(时间戳)的调用都会击中DataBase而不是缓存。
在LastValueCache = Item之前的23 ms期间,对公共项的任何调用都会得到一个过期的23 ms的值。声明的目标是让其他线程看到LastValueCache,但是看到的是一个陈旧的LastValueCache。
移除也是一样的。
您将有几次命中数据库,您本可以避免。
你想达到什么目的?
你描述过这个了吗?
我打赌瓶颈是对数据库的调用。
数据库调用比锁和MemoryBarrier之间的差异长1000倍。
public void Save(Item item)
{
// add logic that the prior asynchonous call to SaveToDatabase is complete
// if not wait for it to complete
// LastValueCache will possible be replaced so you need last item in the database
// the time for a lock is not really a factor as it will be faster than the prior update
Item cached = LastValueCache;
if (cached == null || item.Stamp > cached.Stamp)
{
LastValueCache = item;
}
// make the next a task or background so it does not block
SaveToDatabase(item);
}如果您设置LastValueCache =item,甚至可以将逻辑更改为只等待先前的调用;
但是你需要用某种方式控制数据库
下一步是缓存最后一个X并在Item Get中使用它(时间戳)
数据库是您需要优化的调用
再一次,你需要分析
在此之后,逻辑将变得更加复杂,但将数据库调用提供给BlockingCollection。需要确保最后一个X缓存大于BlockingCollections大小。如果不阻塞,等待BC清除。您需要使用相同的BC进行插入和删除,以便按顺序处理它们。可能会变得足够聪明,以至于您只是不插入具有delete的记录。不要一次只插入或删除一条记录。
发布于 2020-06-17 05:44:32
我实现了一个为并发工作负载设计的线程安全伪LRU : ConcurrentLru。性能非常接近ConcurrentDictionary,比MemoryCache快10倍,命中率优于常规的LRU。在下面的github链接中提供了全面分析。
用法如下:
int capacity = 666;
var lru = new ConcurrentLru<int, SomeItem>(capacity);
var value = lru.GetOrAdd(1, (k) => new SomeItem(k));GitHub:https://github.com/bitfaster/BitFaster.Caching
Install-Package BitFaster.Caching发布于 2014-03-17 21:53:22
对于您的情况,volatile应该是足够的和最具表现力的。在一些潜在的情况下,仅靠volatile并不足以确保读到的内容得到最及时的值,但在这种情况下,我不认为它是一个重要的因素。有关详细信息,请参阅这篇由乔·阿尔巴哈里撰写的精彩文章的“易失性关键字”部分。
https://stackoverflow.com/questions/22465853
复制相似问题