多线程列表模式建议

Multi-threading list pattern advice

我制作了一个包含 folder/file 扫描仪的应用程序。我遇到了线程结构的问题。

工作原理: 对于每个 folder/file 它发现它启动一个线程。每个线程内都有一个函数,它使用一个列表来检查是否找到了类似的项目,以便它可以添加到现有项目中。如果没有找到,它会将项目添加到前面提到的列表中。线程并行执行(异步)。

问题: 因为它是异步的,所以它有时会在列表检查中失败。这是因为检查和添加到列表之间有一个时间段。可能发生的事情是检查 returns 没有类似的项目,而确实有。这将导致相同的项目出现在列表中。

我也让线程互相等待。我真的很喜欢它在前端带来的效果。 (项目很好地实时添加到列表中)。但这需要很长时间 folders/files.

现在我正在考虑混合使用这些函数,但我真的很想看到异步线程的速度和每个线程等待的安全性的结合。

有人知道吗?

您应该锁定检查列表并添加值的整个代码部分。

像这样:

private void YourThreadMethod(object state)
{
    // long taking operation

    lock (dictionary)
    {
        if (!dictionary.ContainsKey(yourItemKey))
        {
            // construct object, long taking operation

            dictionary.Add(yourItemKey, createdObject);
        }
    }
}

这样一来,每个线程都得等到list空闲了才可以使用。如果您想要更高级的解决方案,您可以阅读 ReaderWriterLockSlim class,它提供了更细粒度的解决方案。

我会考虑使用 C# 中的 thread safe collections 之一。对于您的情况,ConcurrentBag 之类的东西比使用锁更有效。

如果检查和添加之间存在时间延迟,您可以使用ConcurrentDictionary。它有一个 TryAdd 方法,如果字典中已经存在具有相同键的项目,该方法将 return false

最圆滑的方法是在 yourItemKeystring 类型时使用 ConcurrentDictionary<string, byte> (otherwise adapt TKey and use a proper IEqualityComparer or implement IEquatable):

private readonly ConcurrentDictionary<string, byte> _list = new ConcurrentDictionary<string, byte>();
private void Foo(object state)
{
  // looong operation
  this._list.TryAdd(yourItemKey, 0);
}
public void Bar()
{
  // this is how to query the content
  this._list.Keys...;
}

这背后的诀窍是不要使用太复杂的对象作为键,它可能需要处理或具有外部引用(我更喜欢任何字符串表示),以及一个小类型的值,它只是作用作为标记。