我有一个api,它是地理坐标请求的终点。这意味着用户可以搜索其区域中的特定位置。同时可以添加新位置。为了使查询尽可能快,我认为我将使R树不可更改。也就是说,R-Tree中没有锁,因为多个线程可以在没有竞争条件的情况下同时读取。更新被收集,例如收集了100个更新,我想创建一个新的R-Tree并替换旧的R-Tree。现在我的问题是如何做到最好?
我有一个SearchService,它以单音形式存储,并具有R-Tree作为私有实例。
在我的Startup.cs中
services.AddSingleton<ISearchService, SearchService>();
ISearchService.cs
public interface ISearchService
{
IEnumerable<GeoLocation> Get(RTreeQuery query);
void Update(IEnumerable<GeoLocation> data);
}
SearchService.cs
public class SearchService : ISearchService
{
private RTree rTree;
public IEnumerable<GeoLocation> Get(RTreeQuery query)
{
return rTree.Get(query);
}
public void Update(IEnumerable<GeoLocation> data)
{
var newTree = new RTree(data);
Interlocked.Exchange<RTree>(ref rTree, newTree);
}
}
我的问题是,如果我与Interlock.Exchange()交换引用,则该操作是原子性的,应该没有竞争条件。但是,如果线程仍然使用旧实例来处理其请求,将会发生什么。可能是垃圾回收器在线程仍访问旧实例时删除了它吗?毕竟,不再有对旧实例的引用。
我对这个话题还比较陌生,欢迎大家提供帮助。感谢您的支持!
对references的读写是atomic,这意味着不存在对齐问题。但是,它们可能会过时。
CLI规范的第12.6.6节
除非显式布局控件(请参阅分区II(控制实例)布局))用于更改默认行为,数据元素否大于自然字大小(本机int的大小)应为正确对齐。对象引用应被视为以本机字大小存储。
关于GC,您的树在运行时是安全的Get
。
因此,总的来说,您的方法就线程而言是安全的,您也可以使用Update
方法并安全地覆盖reference,不需要Interlocked.Exchange
。当前实现中可能发生的最坏情况是,您只会得到一棵陈旧的树,而您提到的这不是问题。