C#并发列表问题
在C#中,我有一个简单类型的列表。此列表可由多个线程访问:可以添加或删除条目,并且可以检查条目是否存在。到目前为止,我已经将列表封装在一个对象中,只公开了这三个操作 我有几个案例要处理(与我刚才提到的方法不完全相同)。C#并发列表问题,c#,concurrency,C#,Concurrency,在C#中,我有一个简单类型的列表。此列表可由多个线程访问:可以添加或删除条目,并且可以检查条目是否存在。到目前为止,我已经将列表封装在一个对象中,只公开了这三个操作 我有几个案例要处理(与我刚才提到的方法不完全相同)。 线程只需检查条目是否存在。(简单) 线程可以检查条目是否存在,如果不存在,则添加该条目。 线程需要检查条目是否存在,如果存在,则等待删除该条目。 2和3的组合,其中线程检查条目是否存在,如果条目确实存在,则必须等到它被删除后才能添加它自己 整个想法是,条目的存在意味着锁。如果条目
另外,是否有可能对这类事情进行单元测试(而不是简单的操作、并发情况)?单元测试肯定很难 这一切都可以通过.NET中的“本机”并发机制合理地完成:lock语句和
Monitor.Wait
/Monitor.pulsell
。除非每个项目都有单独的监视器,否则每次删除任何内容时都需要唤醒所有线程,否则您将无法告诉“正确”的线程唤醒
如果它实际上只是一组项目,那么您可能希望使用HashSet
而不是List
来表示集合,顺便说一句,您提到的与排序无关
示例代码,假设一个集合适合您:
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);
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
公营锁收藏
{
private readonly HashSet items=new HashSet();
私有只读对象挂锁=新对象();
公共布尔包含(T项)
{
锁(挂锁)
{
返回项目。包含(项目);
}
}
公共布尔添加(T项)
{
锁(挂锁)
{
//HashSet.Add已完成所需的操作:)
//请注意,如果项目
//*已添加(即!包含(项目))
返回项目。添加(项目);
}
}
公共无效等待不存在(T项)
{
锁(挂锁)
{
while(items.Contains(item))
{
监视器。等待(挂锁);
}
}
}
公共作废待处理数据(T项)
{
锁(挂锁)
{
等待不存在(项目);
项目。添加(项目);
}
}
公共作废删除(T项)
{
锁(挂锁)
{
如果(项目。移除(项目))
{
监视器。脉冲密封(挂锁);
}
}
}
}
(无可否认,完全未经测试。您可能还想指定等待代码的超时…虽然#1可能是最简单的编写方法,但它本质上是一种无用的方法。除非您在完成“条目的存在性”的查询后持有相同的锁,否则您实际上返回的是“在过去某个时刻存在条目”。它不会给你任何关于当前条目存在的信息
在发现列表中的某个值,然后执行任何检索、删除该值的操作之间,另一个线程可能会来为您删除该值
如果检查为真/假,则并发列表上的包含操作应与您计划执行的操作相结合。例如TestAdd()或TestRemove()比Contains+Add或Contains+Remove安全得多有一种产品可以在单元测试中查找竞争条件等。它叫。不过,我不能说任何支持或反对其有效性的话。:) 这是一个正确的、并发的、线程安全的、可并行的并发列表实现
你应该在线程安全的收集界面上链接到你的博客文章。是的,但是#4就可以做到这一点。其他方法的可用性是值得怀疑的,但不一定是错误的。@Henk,我认为#2-#4是好方法#1虽然没有错,但只是没用。好吧,事情是这样的。在某些情况下,如果条目存在,我需要跳过一些代码并继续执行。@Matt,问题是你不能认为元素存在。你一定认为它是“曾经存在过的”+1,我找到了一个类似的解决方案,但哈希集使它更有效。Monitor类的演示很好,使用了嵌套锁等。唯一的问题可能是删除项时出现“踩踏”。不,该实现对于枚举来说是完全非线程安全的,过度锁定使其成为一个糟糕的选择。