Skip to main content
 首页 » 编程设计

c#之集合被修改;枚举操作可能不会在选择期间执行

2024年04月17日33over140

Unhandled exception has occurred. System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at System.Collections.Generic.List`1.Enumerator.MoveNext()
at System.Linq.Enumerable.WhereSelectListIterator`2.MoveNext()
at System.Linq.Enumerable.Sum(IEnumerable`1 source)
at Check.get_TotalChange()
at CheckChanged(ICheck check)

如果我每天运行我的代码 5000 次,比如 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); 
        } 
    } 

请您参考如下方法:

很可能您在执行 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; 
        } 
    }