C#并发列表问题

时间:2009-07-02 16:50:27

标签: c# concurrency

我在C#中有一个情况,我有一个简单类型的列表。可以通过多个线程访问此列表:可以添加或删除条目,并且可以检查条目是否存在。我已经将列表封装在一个对象中,这个对象到目前为止只暴露了这三个操作。

我有几个案件要处理(与我刚才提到的方法不完全相同)。

  1. 线程可以检查是否存在条目。 (简单)
  2. 线程可以检查是否存在条目,如果不存在,请添加它。
  3. 线程需要检查条目是否存在,如果存在,请等待它被删除。
  4. 2和3的组合,其中一个线程检查是否存在一个条目,如果它存在,它必须等到它被删除才能自己添加它。
  5. 整个想法是条目的存在表示锁定。如果存在条目,则无法更改其标识的对象,并且代码无法继续,因为它正在其他位置进行修改。

    这些似乎是简单的新手情况,但我在并发问题上让自己感到高兴,这让我有点偏执,而且我也不熟悉C#的并发机制。

    处理此问题的最佳方法是什么?我完全没了?应该检查并添加(测试和设置?)组合成第四个原子操作?我只是将锁块添加到访问列表的方法中吗?

    另外,是否有可能对这种事情进行单元测试(不是简单的操作,并发情况)?

4 个答案:

答案 0 :(得分:8)

单元测试肯定很难。

这一切都可以通过.NET中的“本机”并发机制合理地完成:锁定语句和Monitor.Wait / Monitor.PulseAll。除非你的每个项目都有一个单独的监视器,否则你需要在删除任何东西时唤醒所有线程 - 否则你将无法告诉“正确”的线程被唤醒。

如果它只是一个 set 项目,那么您可能希望使用HashSet<T>而不是List<T>代表该集合,顺便说一下 - 你没有提到的是订购。

示例代码,假设一个集合适合您:

using System;
using System.Collections.Generic;
using System.Threading;

public class LockCollection<T>
{
    private readonly HashSet<T> items = new HashSet<T>();
    private readonly object padlock = new object();

    public bool Contains(T item)
    {
        lock (padlock)
        {
            return items.Contains(item);
        }
    }

    public bool Add(T item)
    {
        lock (padlock)
        {
            // HashSet<T>.Add does what you want already :)
            // Note that it will return true if the item
            // *was* added (i.e. !Contains(item))
            return items.Add(item);
        }
    }

    public void WaitForNonExistence(T item)
    {
        lock (padlock)
        {
            while (items.Contains(item))
            {
                Monitor.Wait(padlock);
            }
        }
    }

    public void WaitForAndAdd(T item)
    {
        lock (padlock)
        {
            WaitForNonExistence(item);
            items.Add(item);
        }
    }

    public void Remove(T item)
    {
        lock (padlock)
        {
            if (items.Remove(item))
            {
                Monitor.PulseAll(padlock);
            }
        }
    }
}

(完全未经测试,诚然。您可能还想为等待代码指定超时......)

答案 1 :(得分:8)

虽然#1可能是最简单的,但它本质上是一种无用的方法。除非在完成对“条目存在”的查询后持有相同的锁,否则实际上返回“在过去的某个时刻存在条目”。它没有提供有关当前条目存在的任何信息。

在发现列表中的值然后执行任何检索操作之间,删除该值,另一个线程可能会为您删除它。

包含并发列表上的操作应该与您计划在该检查的真/假存在的情况下执行的操作相结合。例如,TestAdd()或TestRemove() 比包含+添加或包含+删除更安全

答案 2 :(得分:2)

这是一个适当的,并发的,线程安全的,可并行化的并发列表实现 http://www.deanchalk.me.uk/post/Task-Parallel-Concurrent-List-Implementation.aspx

答案 3 :(得分:1)

有一种产品可用于在单元测试中查找竞争条件等。它被称为TypeMock Racer。但是,我无法赞同或反对其有效性。 :)