我有一个“天空队列”对象,我的进程在其中拾取每个对象并执行它。如果需要结果,则将其放入ConcurrentDictionary中,以使用GUID进行检索。每隔几毫秒就会从队列中添加并执行Sky对象,此过程将运行数小时。从ConcurrentDictionary中检索结果会尝试删除并删除对象,但是由于不需要它们,因此可能无法检索其中的一些对象。我已经实现了在不需要结果时不创建结果的想法。为了以防万一,我向每个天空对象添加了一个UTC创建日期。
我想创建一个每隔几分钟运行一次的清理过程,以查找任何早于x分钟的UTC时间并将其删除。据我了解ConcurrentDictionary的介绍,我应该没有问题,只需遍历集合并简单地将它们删除即可,但是在编写清理程序之前,我有几个问题。
我应该在单独的异步任务中运行清理过程吗?请问我 重复或删除原因可避免将任何结果添加到 ConcurrentDictionary,就像阻止问题一样?
因此,我添加了第二个ConcurrentDictionary。当我将结果添加到第一个字典时,我还将GUID和UTC日期添加到第二个字典。下面的代码迭代第二个以及所有发现已从第一个中删除的过期项目。我尚未对此进行测试,因此不确定在重复字典时是否可以从秒中删除。
/// <summary>
/// Use to clean up an sky results objects that might be left in the results dictionary
/// This compares the now UTC date to the object created date plus the seconds parameter
/// </summary>
/// <param name="seconds"></param>
public static void CleanSkyResultDictionary(double seconds)
{
foreach (var skyresult in SkyCleanupDictionary)
{
if (skyresult.Value.AddSeconds(seconds) <= DateTime.UtcNow) continue;
SkyResultDictionary.TryRemove(skyresult.Key, out _);
SkyCleanupDictionary.TryRemove(skyresult.Key, out _);
}
}
答案 0 :(得分:1)
可以从多个线程安全地访问ConcurrentDictionary,而不会破坏内部字典数据结构。因此,您只需要一个字典实例,因为一个线程可以同时添加到另一个字典实例,或者从另一个线程迭代或删除它。
答案 1 :(得分:1)
拥有两个字典意味着现在您应该真正同步它们,这将部分抵消使用并发字典的好处。
我建议将timstamp存储在同一词典中。一种方法是:
class ToStore {
//Constructor here, or add public sets
public YourClass Data {get;}
public DateTime AddedAtUtc {get;}
//I would suggest using NodaTime's Instant, but that's out of scope for this question.
}
public void Add(YourClass data )
{
if (data == null)
{
throw new ArgumentNullException(nameof(data ));
}
var frame = new ToStore {
Data = data,
AddedUtc = DateTime.UtcNow
}
dict.TryAdd(frame.TimestampUtc, frame);
OnAdd(); // fire and forget
}
如果key可以是时间戳,则不需要ToStore
类,这将使其更加简单。
我不认识您的应用程序,但是您可以考虑在添加新元素时而不是在计时器上进行清理。
public void Add(YourOtherClass data )
{
(...)
OnAdd(); // fire and forget
}
private void OnAdd()
{
Task.Run(() =>
{
CleanUp();
}).ConfigureAwait(false);
}
Cleanup
是:
foreach (var kvp in dict.Where(IsStale))
{
// Please note that by now the frame may have been already
// removed by another thread.
dict.TryRemove(kvp.Key, out var ignored);
}
其中IsStale
返回true,如果框架足够旧而可以被移除。
我希望这会有所帮助。