【问题标题】:ConcurrentBag of strings and using .Contains in Parallel.ForEachConcurrentBag 字符串并在 Parallel.ForEach 中使用 .Contains
【发布时间】:2019-02-20 17:01:23
【问题描述】:

我使用 ConcurrentBag 来包含一个字符串列表。有时它会包含一个副本。

但是我在添加新条目之前检查了它的内容,所以它不应该有重复。

ConcurrentDictionary<string, string> SystemFiles = PopulateSystemFiles();
ConcurrentBag<string> SystemNames = new ConcurrentBag<string>();

Parallel.ForEach(SystemFiles, file =>
{
    string name = GetSystemName(file.Value);

    if (!SystemNames.Contains(name))
    {
        SystemNames.Add(name);
    }
});

我的假设是 .Contains 方法不是线程安全的。我说的对吗?

【问题讨论】:

  • 你不能把它全部添加并调用一个 Distinct() 函数吗?
  • 枚举 ConcurrentBag 效率极低。改用 ConcurrentDictionary
  • 集合线程安全的,就像“同时使用它不会破坏它的状态”。它不会使您的“包含然后添加”操作原子化。你的代码不是线程安全的
  • 会的,是的。但是如果你使用锁,那么你可以使用非线程安全的集合,比如 HashSet。最后,我认为你应该用 ConcurrentDictionary 替换你的 ConcurrentBag (这有点尴尬,因为你将只使用键,而不是值,但没关系)
  • @CathalMF ConcurrentDictionary 有一个 TryAdd 方法,它以原子方式执行“如果不包含则添加”操作

标签: c# multithreading concurrency parallel-processing thread-safety


【解决方案1】:

ConcurrentBag 是线程安全的,但您的代码不是:

if (!SystemNames.Contains(name))
{
    SystemNames.Add(name);
}

Contains 将以线程安全的方式执行,然后Add 也将以线程安全的方式执行,但您不能保证中间没有添加项目。

根据您的需要,我建议改用ConcurrentDictionary。忽略该值,因为您不需要它。

var SystemNames = new ConcurrentDictionary<string, bool>();

然后使用TryAdd 方法在单个原子操作中执行“如果不包含则添加”:

SystemNames.TryAdd(name, true);

【讨论】:

    猜你喜欢
    • 2011-11-29
    • 1970-01-01
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    相关资源
    最近更新 更多