从ConcurrentDictionary中删除项目

时间:2018-10-10 23:34:01

标签: c# concurrentdictionary

我有一个“天空队列”对象,我的进程在其中拾取每个对象并执行它。如果需要结果,则将其放入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 _);
        }
    }

2 个答案:

答案 0 :(得分:1)

可以从多个线程安全地访问ConcurrentDictionary,而不会破坏内部字典数据结构。因此,您只需要一个字典实例,因为一个线程可以同时添加到另一个字典实例,或者从另一个线程迭代或删除它。

答案 1 :(得分: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类,这将使其更加简单。

2。清理

我不认识您的应用程序,但是您可以考虑在添加新元素时而不是在计时器上进行清理。

    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,如果框架足够旧而可以被移除。

我希望这会有所帮助。