我正在为以下场景搜索正确的线程安全集合(并发集合):
我可能会从外部源请求生成GUID(因此它是唯一且非重复的)。我需要存储(比如最后100个请求)并检查是否传递了重复的GUID。由于某些限制,我可能不会将所有GUID保存超过100个。
现在的问题是,当在服务中使用此机制时,它必须绑定到100个项目,并且基于GUID进行搜索至关重要。
我决定使用ConcurrentDictionary
但我怀疑这是一个很好的决定,因为我可以在使用整个100个插槽后更改密钥。我可能会找到一种很好的机制来在字典填满时替换最旧的请求。
非常感谢任何想法。
提供了一个代码段来显示我未完成的实现
public static ConcurrentDictionary<string, TimedProto> IncidentsCreated = new ConcurrentDictionary<string, TimedProto>(20, 100);
private static bool AddTo_AddedIncidents(proto ReceivedIncident)
{
try
{
int OldestCounter = 0;
DateTime OldestTime = DateTime.Now;
if (IncidentsCreated.Count < 100)
{
TimedProto tp = new TimedProto();
tp.IncidentProto = ReceivedIncident;
tp.time = DateTime.Now;
IncidentsCreated.AddOrUpdate(ReceivedIncident.IncidentGUID, tp,
(s,i) => i);
return true;
}
else //array is full, a replace oldest mechanism is required
{
}
return true;
}
catch (Exception ex)
{
LogEvent("AddTo_AddedIncidents\n"+ex.ToString(), EventLogEntryType.Error);
return false;
}
}
public struct proto
{
public string IncidentGUID;
//other variables
}
public struct TimedProto
{
public proto IncidentProto;
public DateTime time;
}
由于
答案 0 :(得分:1)
由于您使用的粒度为15毫秒的DateTime,因此您的实现存在缺陷。这意味着如果流入量很大,您甚至可能会意外删除最近的guid。
public class LruCache<TKey, TValue>
{
private readonly int _capacity;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
class Reference<T> where T : struct
{
public T Value;
}
private class Node
{
public TValue Value;
public volatile Reference<long> Ticks;
}
private readonly ConcurrentDictionary<TKey, Node> _nodes = new ConcurrentDictionary<TKey, Node>();
public LruCache(int capacity)
{
Debug.Assert(capacity > 10);
_capacity = capacity;
}
public void Set(TKey key, TValue value)
{
var node = new Node
{
Value = value,
Ticks = new Reference<long> { Value = _stopwatch.ElapsedTicks }
};
_nodes.AddOrUpdate(key, node, (_, __) => node);
if (_nodes.Count > _capacity)
{
foreach (var source in _nodes.OrderBy(x => x.Value.Ticks).Take(_nodes.Count / 10))
{
Node _;
_nodes.TryRemove(source.Key, out _);
}
}
}
public bool TryGet(TKey key, out TValue value)
{
Node node;
if (_nodes.TryGetValue(key, out node))
{
node.Ticks = new Reference<long> {Value = _stopwatch.ElapsedTicks};
value = node.Value;
return true;
}
value = default(TValue);
return false;
}
}
答案 1 :(得分:0)
我会使用循环缓冲区 - 有很多实现,包括this one,并为其中一个做一个线程安全的包装并不难。
只有100个左右的插槽,按键查找会相当有效,并且插入效率非常高(不会重新分配,因为旧项目被丢弃并被新项目替换)。