C#并发列表问题

Mat*_*t H 8 c# concurrency

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

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

  1. 线程只能检查是否存在条目.(简单)
  2. 线程可以检查条目是否存在,如果不存在,则添加它.
  3. 线程需要检查条目是否存在,如果存在,则等待它被删除.
  4. 2和3的组合,其中一个线程检查是否存在一个条目,如果它确实存在,它必须等到它被删除才能自己添加它.

整个想法是条目的存在表示锁定.如果存在条目,则无法更改其标识的对象,并且代码无法继续,因为它正在其他位置进行修改.

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

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

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

Jon*_*eet 8

单元测试肯定会很难.

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

如果它实际上只是一项目,那么您可能希望使用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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Jar*_*Par 8

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

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

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