Коллекция была изменена; операция перечисления может не выполняться

Для написания приложений баз данных на Python необходимо выполнить следующие пять шагов:

Импортировать SQL-интерфейс с помощью следующей команды:

>>> import MySQLdb

Установить соединение с базой данных с помощью следующую команду:

conn = MySQLdb.connect (host = 'localhost', user = 'root', passwd = '') ... где host - это имя ваш хост-компьютер, за которым следуют имя пользователя и пароль. В случае корня нет необходимости указывать пароль.

Создайте курсор для соединения со следующей командой:

>>>cursor = conn.cursor()

Выполните любой SQL-запрос, используя этот курсор, как показано ниже: здесь выходы в терминах показывают количество строк, затронутых этим запросом:

курсор. execute («Создать библиотеку базы данных») // Указывает, сколько строк затронуто

>>> cursor.execute('use Library')

>>>table='create table books(book_accno char(30) primary key, book_name
char(50),no_of_copies int(5),price int(5))'
>>> cursor.execute(table)

Наконец, выберите набор результатов и выполните итерацию по этому набору результатов. На этом этапе пользователь может получить результирующие наборы, как показано ниже:

>>> cursor.execute('select * from books')
>>> cursor.fetchall()

((«Py9098», «Программирование с помощью Python», 100L, 50L), («Py9099», «Программирование с Python ', 100L, 50L))

816
задан cdonner 5 July 2014 в 02:57
поделиться

6 ответов

То, что, вероятно, происходит, - то, что SignalData косвенно изменяет словарь подписчиков под капотом во время цикла и ведет к тому сообщению. Можно проверить это путем изменения

foreach(Subscriber s in subscribers.Values)

К

foreach(Subscriber s in subscribers.Values.ToList())

, Если я буду прав, то проблема будет вызывающие абоненты dissapear

. Значения. ToList () копирует значения подписчиков. Значения к отдельному списку в начале foreach. Ничто иное не имеет доступ к этому списку (он даже не имеет имени переменной!), таким образом, ничто не может изменить его в цикле.

1506
ответ дан Blueriver 5 July 2014 в 13:57
поделиться

Когда подписчик отказывается от подписки, Вы изменяете содержание набора Подписчиков во время перечисления.

существует несколько способов зафиксировать это, при этом один изменяющийся, чтобы цикл использовал явное .ToList():

public void NotifySubscribers(DataRecord sr)  
{
    foreach(Subscriber s in subscribers.Values.ToList())
    {
                                              ^^^^^^^^^  
        ...
109
ответ дан Mitch Wheat 5 July 2014 в 13:57
поделиться
  • 1
    Мы просто развернули его в производство. Так я haven' t получил хорошую статистику до утечек памяти. До загружающейся скорости - после уменьшения приложения и платформы - we' ре, говорящее о загрузке в браузер (в первый раз) общий размер файлов 2-4 приблизительно 2M... И я думаю, что сама платформа соответствует приблизительно половине из него. В зависимости от Ваших скоростей поставщика - it' s приблизительно 10-15 секунд. – sha 12 July 2012 в 17:51

Более эффективный путь, по-моему, состоит в том, чтобы иметь другой список, что Вы объявляете помещение чего-либо, что должно "быть удалено" в. Тогда после окончания основного цикла (без.ToList ()), Вы переделываете другой цикл, "чтобы быть удаленными" список, удаляя каждую запись, как это происходит. Таким образом в Вашем классе Вы добавляете:

private List<Guid> toBeRemoved = new List<Guid>();

Тогда Вы изменяете его на:

public void NotifySubscribers(DataRecord sr)
{
    toBeRemoved.Clear();

    ...your unchanged code skipped...

   foreach ( Guid clientId in toBeRemoved )
   {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                e.Message);
        }
   }
}

...your unchanged code skipped...

public void UnsubscribeEvent(Guid clientId)
{
    toBeRemoved.Add( clientId );
}

Это не только решит Вашу проблему, она будет препятствовать тому, чтобы Вы имели, чтобы продолжать создавать список из Вашего словаря, который является дорогим, если существует много подписчиков там. Принятие списка подписчиков, которые будут удалены на любом данном повторении, ниже, чем общее количество в списке, это должно быть быстрее. Но, конечно, не стесняйтесь представлять его, чтобы быть уверенными, что это имеет место, если существует сомнение в Вашей определенной ситуации с использованием.

62
ответ дан Soner Gönül 5 July 2014 в 13:57
поделиться
  • 1
    гм.. хорошие звуки, был то, что по Интернету как насчет начального времени загрузки ExtJS? Если это был внешний клиент, сталкивающийся с приложением - сделал Вас, столкнулся с какими-либо проблемами утечки памяти браузера до сих пор? – deej 12 July 2012 в 17:49

Принятый ответ является неточным и неправильным в худшем случае. , Если изменения внесены во время ToList() , можно все еще закончить с ошибкой. Кроме того lock, какая производительность и потокобезопасность должны быть учтены, если у Вас есть общедоступный участник, надлежащее решение может использовать неизменные типы .

В целом, неизменный тип означает, что Вы не можете изменить состояние его когда-то созданный. Таким образом, Ваш код должен быть похожим:

public class SubscriptionServer : ISubscriptionServer
{
    private static ImmutableDictionary<Guid, Subscriber> subscribers = ImmutableDictionary<Guid, Subscriber>.Empty;
    public void SubscribeEvent(string id)
    {
        subscribers = subscribers.Add(Guid.NewGuid(), new Subscriber());
    }
    public void NotifyEvent()
    {
        foreach(var sub in subscribers.Values)
        {
            //.....This is always safe
        }
    }
    //.........
}

Это может быть особенно полезно, если у Вас есть общедоступный участник. Другие классы всегда могут foreach на неизменных типах, не вызывая беспокойство об изменяемом наборе.

0
ответ дан 22 November 2019 в 21:09
поделиться

Вот определенный сценарий, который гарантирует специализированный подход:

  1. Эти Dictionary часто перечисляется.
  2. Эти Dictionary нечасто изменяется.

В этом сценарии, создающем копию Dictionary (или Dictionary.Values), прежде чем, каждое перечисление может быть довольно дорогостоящим. Моя идея о решении этой проблемы состоит в том, чтобы снова использовать ту же кэшируемую копию в нескольких перечислениях и смотреть IEnumerator из оригинала Dictionary для исключений. Перечислитель будет кэшироваться наряду со скопированными данными и опрашиваться прежде, чем запустить новое перечисление. В случае исключения будет отброшена кэшируемая копия, и будет создан новый. Вот моя реализация этой идеи:

public class SafeEnumerable<T> : IEnumerable<T>, IDisposable
{
    private IEnumerable<T> _source;
    private IEnumerator<T> _enumerator;
    private T[] _cached;

    public SafeEnumerable(IEnumerable<T> source)
    {
        _source = source ?? throw new ArgumentNullException(nameof(source));
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (_source == null) throw new ObjectDisposedException(this.GetType().Name);
        if (_enumerator == null)
        {
            _enumerator = _source.GetEnumerator();
            _cached = _source.ToArray();
        }
        else
        {
            var modified = false;
            if (_source is ICollection collection) // C# 7 syntax
            {
                modified = _cached.Length != collection.Count;
            }
            if (!modified)
            {
                try
                {
                    _enumerator.MoveNext();
                }
                catch (InvalidOperationException)
                {
                    modified = true;
                }
            }
            if (modified)
            {
                _enumerator.Dispose();
                _enumerator = _source.GetEnumerator();
                _cached = _source.ToArray();
            }
        }
        foreach (var item in _cached)
        {
            yield return item;
        }
    }

    public void Dispose()
    {
        _enumerator?.Dispose();
        _enumerator = null;
        _cached = null;
        _source = null;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public static SafeEnumerable<T> AsSafeEnumerable<T>(this IEnumerable<T> source)
    => new SafeEnumerable<T>(source);

пример Использования:

private static IDictionary<Guid, Subscriber> _subscribers;
private static SafeEnumerable<Subscriber> _safeSubscribers;

//...(in the constructor)
_subscribers = new Dictionary<Guid, Subscriber>();
_safeSubscribers = _subscribers.Values.AsSafeEnumerable();

// ...(elsewere)
foreach (var subscriber in _safeSubscribers)
{
    //...
}

, К сожалению, эта идея не может в настоящее время использоваться с классом Dictionary в.NET Core 3.0, потому что этот класс не бросает , Набор был изменен исключение при перечислении и методы Remove и Clear вызываются. Все другие контейнеры, которые я проверил, ведут себя последовательно. Я систематически проверял эти классы: List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> и SortedDictionary<T,V>. Только два вышеупомянутых метода Dictionary класс в.NET Core не делают недействительным перечисление.

<час>

Обновление: я решил вышеупомянутую проблему путем сравнения также длин кэшируемого и исходного набора. Эта фиксация предполагает, что словарь будет передан непосредственно как аргумент SafeEnumerable конструктор, и его идентификационные данные не будут скрыты (например), проекцией как: dictionary.Select(e => e).AsSafeEnumerable().

0
ответ дан 22 November 2019 в 21:09
поделиться

Я лично недавно натыкался на это в незнакомом коде, где это было в открытом dbcontext с.Remove Linq (объект). У меня было адское время, определяя местоположение ошибки при отладке, потому что объект (объекты) был удален, в первый раз выполнив итерации, и если я пытался отступить и повторно переступить, набор был пуст, так как я был в том же контексте!

-1
ответ дан 22 November 2019 в 21:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: