2015-05-21 28 views
2

我制作了一个应用程序,其中还包含一个文件夹/文件扫描程序。我遇到了线程结构的问题。多线程列表模式建议

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

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

我也已经让线程彼此等待。我非常喜欢它在前端的效果。 (项目很好地实时添加到列表中)。但是这需要很长的时间来处理很多文件夹/文件。

现在我正在考虑在函数之间进行混合,但我真的很希望看到异步线程的速度和等待每个线程的安全性。

有人有什么想法吗?

回答

3

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

事情是这样的:

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这个类,它提供了一个更精细的解决方案。

+0

为什么'ConcurrentDictionary '? –

+0

@AndreasNiedermair:因为这并不妨碍检查与延迟添加问题。 –

+0

你只是围绕一个非原子操作构造一个锁,使其成为“原子” - 与TryAdd有什么不同?我错过了什么 - *这将导致相同的项目发生在列表中。*这正是“TryAdd”的原因......? –

0

我会考虑在C#中使用thread safe collections之一。对于你的情况,像ConcurrentBag将比使用锁更有效。

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

+0

当检查和添加之间有时间时,这不是一个解决方案。 –

+0

不错,你可以用'ConcurrentDictionary'来做到这一点,不过它会执行检查并添加一个原子操作。 –

+0

@NedStoyanov我不会把它称为* atomic *本身,因为它不是一个操作。内部使用锁定机构来确保不受干扰。锁的获取确实是一个原子操作(取决于处理器),但'TryAdd'只是*线程安全的:) –

2

最光滑的方法是ConcurrentDictionary<string, byte>的使用时yourItemKey是类型的stringotherwise adapt TKey and use a proper IEqualityComparerimplement 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...; 
} 

背后诀窍是不使用太复杂对象作为密钥,这可能需要处置或具有外部引用(我倾向于任何字符串表示形式)以及值的小类型,它只是用作标记。