并发访问通用列表属性

时间:2016-05-04 14:24:23

标签: c# concurrency

我正在通过验证器列表验证请求对象:

foreach (var validator in validators)
{
    results.Add(validator.Validate(request));
}

每个验证器检查对象,可能会或可能不会向请求对象的错误列表添加错误(List< OrderWarningError>)

public override bool? Validate(Request request)
{
    bool isValid = true;

    if (someErrorConditionIsMet)
    {
        isValid = false;
        request.WarningsErrors.Add(new OrderWarningError
        {
            Message = "The request contains an error",
            Severity = EnumWarningErrorSeverity.Error
        });
    }

    return isValid;
}

我现在正在添加更多验证器,每个验证器可能依赖于其业务规则的外部数据,因此我希望同时运行每个验证器以提高性能。在Add()期间同步访问通用列表属性而不更改其数据类型的最佳方法是什么(这是否可能)?

我可以这样做吗?:

lock (request.WarningsErrors)
{
    request.WarningsErrors.Add(new OrderWarningError
    {
        Message = "The request contains an error",
        Severity = EnumWarningErrorSeverity.Error
    });
}

2 个答案:

答案 0 :(得分:2)

您可以使用lock yes,但最好使用类似于名称空间“System.Collections.Concurrent”中定义的线程安全集合。

答案 1 :(得分:1)

对于集合的并发访问,有一些问题可能会出错,所以让我们看看它们是否适用:

  • 竞争条件:症状包括双重条目,因为另一个帖子在您检查它之间插入一个项目以查看它是否存在以及您添加它的时间。此外,您可能会遇到编辑另一个线程已删除的项目的问题。 在您的情况下,每个验证器只添加0或1个项目,订单无关紧要,因此这不是真正的问题。
  • 死锁:基本上,如果您在集合周围添加锁定,并且您有一个线程正在等待添加另一个线程阻塞的内容并等待添加它。没有解决方案,因此两个线程都被卡住了。 由于您尚未添加锁定,因此这也不是问题。如果这样做,则必须锁定对集合的每次访问。
  • ConcurrentModificationExceptions:如果您尝试在另一个线程修改它时迭代一个集合,那么一次迭代将收到ConcurrentModificationException。注意:如果您在同一个线程中迭代它时尝试修改集合,则可以获取这些集合。

在您的方案中,您正在添加每个验证程序唯一的错误等。只要订单不重要,您就不需要锁定您的收藏。在尝试迭代集合之前,您只需确保完成处理。 在这种特殊情况下,您可以使用任何集合来存储结果。

为了提高安全性,以及在发生潜在修改时进行迭代的能力,您可能需要查看ConcurrentBagConcurrentQueue。 (两者都在System.Collections.Concurrent中)。

  

请记住,并行处理您的验证可能无法为您提供您可能认为会获得的性能优势。

为了使并行处理有意义,您必须处理阻塞I / O或其他导致验证需要很长时间的资源密集型任务。如果您的整个验证时间不到100毫秒(0.1秒),那么并行处理只会使挂钟时间花费更长时间,代价更复杂。

在迭代错误列表之前确保所有处理完成的最简单的构造是Parallel.ForEach构造。但我建议使用Stopwatch来检验假设。