收集被修改;在select期间可能不会执行枚举操作

时间:2016-08-24 08:39:57

标签: c# .net linq

  

发生了未处理的异常。 System.InvalidOperationException:集合已被修改;枚举操作可能无法执行       在System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource   资源)
      在System.Collections.Generic.List`1.Enumerator.MoveNextRare()
      在System.Collections.Generic.List`1.Enumerator.MoveNext()
      在System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
      在System.Linq.Enumerable.Sum(IEnumerable`1 source)
      在Check.get_TotalChange()
      在CheckChanged(ICheck check)

如果我每天运行我的代码5次,就像2次那样会导致此错误,程序会崩溃..我的代码出了什么问题?我遵循了异常信息,我认为这是可能导致异常的代码。任何人都知道是什么类型的问题?

public decimal TotalChange
{
    get
    {
        var change = (from IPayment payment in _payments where payment.Amount < 0 select payment.Amount).Sum();
        var paid = (from IPayment payment in _payments select payment.Amount).Sum();
        return paid <= Total && Total > 0 ? Math.Abs(change) : paid - Total;
    }
}

我把它改为:

    private readonly object _totalChangeLock = new object();
    public decimal TotalChange
    {
        get
        {
            lock (_totalChangeLock)
            {
                var change =
                    (from IPayment payment in _payments where payment.Amount < 0 select payment.Amount).Sum();
                var paid = (from IPayment payment in _payments select payment.Amount).Sum();
                return paid <= Total ? Math.Abs(change) : paid - Total;
            }
        }
    }

我也有2种修改_payments的方法。我也锁定了它们。希望不会引起任何问题。

    public void AddPayment(IPayment payment)
    {
        lock (_paymentsLock)
        {
            if (_payments.Contains(payment)) return;
            _payments.Add(payment);
        }
    }

    public void RemovePayment(IPayment payment)
    {
        lock (_paymentsLock)
        {
            if (_payments.Contains(payment))
                _payments.Remove(payment);
        }
    }

2 个答案:

答案 0 :(得分:2)

最有可能在执行TotalChange属性代码时从另一个线程修改_payments变量。

如果是这样,请尝试避免对_payments变量进行多线程访问,或者在对_payments变量进行读/写访问时考虑使用锁定

lock(_lockerObject)
{
    // Do operations that modify _payments collection
    _payments.Add();//...
}

    public decimal TotalChange
    {
        get
        {
            lock (_lockerObject)
            {
                var change = (from IPayment payment in _payments where payment.Amount < 0 select payment.Amount).Sum();
                var paid = (from IPayment payment in _payments select payment.Amount).Sum();
            }
            return paid <= Total && Total > 0 ? Math.Abs(change) : paid - Total;
        }
    }

答案 1 :(得分:1)

有很多方法可以解决这个问题,最简单的方法是处理另一个列表,这样就可以添加_payments

public decimal TotalChange
{
    get
    {
        lock(_syncObj)
        {
           var p = _payments.ToList();
        }            

        var change = (from IPayment payment in p where payment.Amount < 0 select payment.Amount).Sum();
        var paid = (from IPayment payment in p select payment.Amount).Sum();
        return paid <= Total && Total > 0 ? Math.Abs(change) : paid - Total;
    }
}

您还需要在添加项目时将其锁定:

lock(_syncObj)
{
    _payments.Add();//...
}