InvalidOperationException: коллекция была изменена - хотя блокировка коллекции - PullRequest
7 голосов
/ 23 апреля 2011

У меня есть синхронизированная хеш-таблица, из которой я регулярно удаляю некоторые записи.Несколько потоков запускают этот код.Поэтому я блокирую весь foreach, но иногда получаю InvalidOperationException: Collection был изменен ... в Hashtable.HashtableEnumerator.MoveNext () - т.е. в цикле foreach.Что я делаю неправильно?Не достаточно ли блокировки?

<code>private static readonly Hashtable sessionsTimeoutData = Hashtable.Synchronized(new Hashtable(5000));</p>

<p>private static void ClearTimedoutSessions()
{
    List keysToRemove = new List();
    long now = DateTime.Now.Ticks;
    lock (sessionsTimeoutData)
    {
        TimeoutData timeoutData;
        foreach (DictionaryEntry entry in sessionsTimeoutData)
        {
            timeoutData = (TimeoutData)entry.Value;
            if (now - timeoutData.LastAccessTime > timeoutData.UserTimeoutTicks)
                keysToRemove.Add((ulong)entry.Key);
        }
    }
    foreach (ulong key in keysToRemove)
        sessionsTimeoutData.Remove(key);
}

Ответы [ 3 ]

8 голосов
/ 23 апреля 2011

Вы хотите заблокировать, используя SyncRoot, который является объектом, для которого будут заблокированы методы для синхронизированной Hashtable:

lock (sessionsTimeoutData.SyncRoot)
{
    // ...
}

См. http://msdn.microsoft.com/en-us/library/system.collections.hashtable.synchronized.aspx:

Перечисление через коллекцию по сути не потокобезопасный процедура. Даже когда коллекция синхронизированы, другие потоки все еще могут изменить коллекцию, которая вызывает перечислитель для исключения. Чтобы гарантировать безопасность нити во время перечисление, вы можете заблокировать Коллекция в течение всего Перечисление или ловить исключения в результате изменений, сделанных другими нитки.

В следующем примере кода показано, как заблокировать коллекцию с помощью SyncRoot в течение всего перечисление:

Hashtable myCollection = new Hashtable();
lock(myCollection.SyncRoot)
{
    foreach (object item in myCollection)
    {
        // Insert your code here.
    }
}
2 голосов
/ 23 апреля 2011

Почему второй foreach вне замка?

1 голос
/ 23 апреля 2011

Вам нужно заблокировать как во время удаления, так и во время расчета того, что удалить. Переместите это,

foreach (ulong key in keysToRemove)
        sessionsTimeoutData.Remove(key);

В ваш заблокированный раздел.

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