从 ConcurrentDictionary 中删除项目
Removing items from a ConcurrentDictionary
我有 Sky 对象队列,我的进程在其中拾取每个对象并执行它。如果需要结果,则将它们放入 ConcurrentDictionary 以使用 GUID 检索。天空对象每隔几毫秒就会从队列中添加和执行,这个过程将 运行 持续数小时。从 ConcurrentDictionary 检索结果会尝试删除并删除对象,但有些可能不会检索到,因为它们不需要。如果不需要,我已经实现了不创建结果的想法。作为以防万一的计划,我为每个天空对象添加了一个 UTC 创建日期。
我想创建一个清理进程,每隔几分钟 运行 查找任何早于 x 分钟的 UTC 时间并将其删除。据我了解 ConcurrentDictionary 我应该没有问题只是遍历集合并简单地删除它们,但在编写我的清理程序之前我有几个问题。
Should I run the cleanup process in a separate async task? Will my
iterating or removing cause keep any results from being added to the
ConcurrentDictionary, like blocking issues?
所以,我添加了第二个 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 _);
}
}
可以从多个线程安全地访问 ConcurrentDictionary,而不会损坏内部字典数据结构。因此,您只需要一个字典实例,因为一个线程可以同时向其中添加内容,而另一个线程则可以对其进行迭代或从中删除。
1。数据
拥有两个词典意味着现在您应该真正同步它们,这将部分抵消使用并发词典的好处。
我建议将时间戳存储在同一个字典中。一种方法是:
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
class 这样会更简单。
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
returns 如果框架足够旧可以删除,则为真。
希望对您有所帮助。
我有 Sky 对象队列,我的进程在其中拾取每个对象并执行它。如果需要结果,则将它们放入 ConcurrentDictionary 以使用 GUID 检索。天空对象每隔几毫秒就会从队列中添加和执行,这个过程将 运行 持续数小时。从 ConcurrentDictionary 检索结果会尝试删除并删除对象,但有些可能不会检索到,因为它们不需要。如果不需要,我已经实现了不创建结果的想法。作为以防万一的计划,我为每个天空对象添加了一个 UTC 创建日期。
我想创建一个清理进程,每隔几分钟 运行 查找任何早于 x 分钟的 UTC 时间并将其删除。据我了解 ConcurrentDictionary 我应该没有问题只是遍历集合并简单地删除它们,但在编写我的清理程序之前我有几个问题。
Should I run the cleanup process in a separate async task? Will my iterating or removing cause keep any results from being added to the ConcurrentDictionary, like blocking issues?
所以,我添加了第二个 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 _);
}
}
可以从多个线程安全地访问 ConcurrentDictionary,而不会损坏内部字典数据结构。因此,您只需要一个字典实例,因为一个线程可以同时向其中添加内容,而另一个线程则可以对其进行迭代或从中删除。
1。数据
拥有两个词典意味着现在您应该真正同步它们,这将部分抵消使用并发词典的好处。
我建议将时间戳存储在同一个字典中。一种方法是:
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
class 这样会更简单。
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
returns 如果框架足够旧可以删除,则为真。
希望对您有所帮助。