C# 以CPU友好的方式在超时后从字典中删除项

C# 以CPU友好的方式在超时后从字典中删除项,c#,concurrency,cpu-usage,C#,Concurrency,Cpu Usage,我有一个用例,在这个用例中,当达到一定数量的项时(例如,字典中最古老的项的一半将被删除),或者当项在字典中停留10秒时,我需要从字典中删除项 新项目会不断添加到字典中,我之所以在这里使用concurrentdirectionary,是为了在流式处理的同时拥有尽可能多的不同项目集 我已经通过使用类似于ConcurrentDictionary的设置实现了这一点,其中我使用message的key(类似于一个最大3个字段的小json)作为dictionary的key,使用message和存储为元组的时间

我有一个用例,在这个用例中,当达到一定数量的项时(例如,字典中最古老的项的一半将被删除),或者当项在字典中停留10秒时,我需要从字典中删除项

新项目会不断添加到字典中,我之所以在这里使用
concurrentdirectionary
,是为了在流式处理的同时拥有尽可能多的不同项目集

我已经通过使用类似于
ConcurrentDictionary
的设置实现了这一点,其中我使用message的key(类似于一个最大3个字段的小json)作为dictionary的key,使用message和存储为元组的时间作为dictionary的值。我现在可以使用这个键来检查重复项,有一个溢出场景,我可以像这样处理(不是实际的代码,通过内存编写)

foreach(dictionary.OrderByDescending中的var项(kvp=>kvp.Value.Item2.Take)(阈值/2))
{
TryRemove(kvp.Key,out var);
//添加到输出队列
}
我会用下面的代码检查旧项目

foreach(var-kvpin字典)
{
if(DateTime.UtcNow.Subtract(kvp.Value.Item2)>=TimeSpan.FromSeconds(10))
{
dictionary.TryRemove(distinctMessageKVP.Key,out var)
//对移除的项目进行处理
}
}
这是有效的。问题是,这是非常CPU密集型的。我计划通过切换到单线程字典访问并使用字典而不是并发字典来减少CPU使用。然后,我还计划增加阈值,这样我们就不会继续传输项目和浪费周期。我还怀疑遍历所有项以从字典中删除项是一个昂贵的过程。如果是这样的话,有没有其他方法可以从字典中删除条目?有没有其他数据结构可以帮助我解决这个问题

编辑:现在,我们有5个任务写入
ConcurrentDictionary
,还有1个任务从dictionary中删除内容。如果这是一个更好的设置(以避免锁定),我将让5个任务写入一个
ConcurrentQueue
,然后将1个任务写入
Dictionary
(非并发),并让同一个任务也删除字典


EDIT2:我们现在看到的是大约每秒有1000个条目进出字典。

这个问题似乎是使用图书馆的一个诱人的例子。存在一个相关问题,其中包含此自定义实现:

public static IObservable<T> DistinctFor<T>(this IObservable<T> src,
    TimeSpan validityPeriod)
public static IObservable distinct(此IObservable src,
TimeSpan validityPeriod)
不过,它不包括逐出旧条目的要求

另一个可以作为易于使用的类提供所需功能的库是。有点像一辆汽车。但我看不到任何实现存在

回到您现有的代码,我建议有两个主要的优化:

  • 不要按降序排列
    ConcurrentDictionary
    的内容。分拣是一项昂贵的操作。使用
    foreach
    的简单枚举对于 查找并删除超时条目

  • 避免频繁调用
    DateTime.UtcNow
    ,这也相当昂贵。在您的情况下,您实际上并不关心每个条目输入的确切日期和时间(无论是白天还是晚上,周一还是周末都没有区别)。你只是对它的年龄感兴趣。因此,不要存储<代码>数据> /代码>字段,我会考虑存储<代码> TimeSpAs/COD>,由<代码>秒表< /代码>生成。AFAIK访问属性比访问
    DateTime.UtcNow
    更有效。如果遵循此路径,则每次读取此属性时可能都应该使用
    ,因为存在争议


  • 评论不用于扩展讨论;你可以考虑使用一个字典来保存插入项的顺序,这样你就可以直接枚举它,并先获得旧条目。有这样一个
    OrderedDictionary
    的实现。它不是线程安全的,因此您必须衡量单个锁引起的争用是否超过了消除排序需求的好处。