Update collection based on timer throws 'Collection was modified; enumeration operation may not execute.'

喜欢而已 提交于 2019-12-13 03:55:52

问题


I'm using Reactive Extensions and ReactiveUI to update a collection of Process objects periodically.

When a checkbox is checked the property Processes, which is bound to a DataGrid, is filled and a timer is set to update every 200ms all processes. Processes that have exited are removed.

The Collection was modified; enumeration operation may not execute. exception is sometimes thrown when doing a foreach. I don't understand, because I am not removing or adding objects to the collection when iterating (just setting a property of the ProcessModel)

Also does the timer wait until everything is completed before firing again?

ViewModel

private ReactiveList<IProcessModel> _processes = new ReactiveList<IProcessModel>() { ChangeTrackingEnabled = true };
public ReactiveList<IProcessModel> Processes { get { return _processes; } }

IDisposable timer;

private void DoShowProcesses(bool checkboxChecked)
{
    Processes.Clear();
    if (checkboxChecked)
    {
        //checkbox checked
        lock (Processes)
            Processes.AddRange(_monitorService.GetProcesses());
        timer = Observable.Timer(TimeSpan.FromMilliseconds(200.0))
            .Select(x =>
        {
            lock (Processes)
            {
                foreach (var process in Processes) //throws the 'Collection was modified; enumeration operation may not execute.'
                    process.UpdateMemory(); 

                return Processes.Where(p => p.ProcessObject.HasExited).ToList();
            }
        }).
        ObserveOnDispatcher()
        .Subscribe(processesExited =>
        {
            if (processesExited.Count() > 0)
            {
                lock (Processes)
                    Processes.RemoveAll(processesExited); //remove all processes that have exited
            }

        });
    }
    else
    {
        if (timer != null)
            timer.Dispose();
    }
}

ProcessModel

public class ProcessModel : ProcessModelBase, IProcessModel
{

    public ProcessModel(Process process)
    {
        ProcessObject = process;

    }

    public void UpdateMemory()
    {
        try
        {
            if (!ProcessObject.HasExited)
            {
                long mem = ProcessObject.PagedMemorySize64;
                ProcessObject.Refresh();
                if (mem != ProcessObject.PagedMemorySize64)
                    OnPropertyChanged(nameof(ProcessObject));
            }
        }
        catch (Exception)
        {
            //log it
        }
    }

回答1:


It is best to post a minimal, complete, verifiable example to help you. This isn't possible to debug or test as written.

As a guess, I would assume you have some sort of handler which is being triggered by the OnPropertyChanged(nameof(ProcessObject)); call. This handler probably removes and possibly re-adds the object from the enumerable. To test, you can detach the handler, or remove the call and see if the exception still occurs.



来源:https://stackoverflow.com/questions/50157185/update-collection-based-on-timer-throws-collection-was-modified-enumeration-op

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!