【问题标题】:InvalidOperationException: Collection was modified - although locking the collectionInvalidOperationException:集合已修改 - 尽管锁定了集合
【发布时间】:2011-08-11 08:58:47
【问题描述】:

我有一个同步的哈希表,我会定期从中删除一些条目。多个线程运行此代码。所以我锁定了整个 foreach,但有时我仍然会得到 InvalidOperationException: Collection was modified ... at Hashtable.HashtableEnumerator.MoveNext() - 即在 foreach 循环中。 我究竟做错了什么?锁定还不够吗?

private static readonly Hashtable sessionsTimeoutData = Hashtable.Synchronized(new Hashtable(5000));

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); }

【问题讨论】:

  • 专业提示:除非您被困在 .NET 1.1 中,否则请使用通用版本。它们更快、更安全、更好。

标签: c# multithreading collections


【解决方案1】:

您想使用 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.
    }
}

【讨论】:

  • 这是个好主意,但真正的问题是第二个 foreach(删除数据的那个)在锁之外。
  • @Etienne:每个Remove() 调用都将被同步(将在内部获取锁)。集合将保持一致 - 集合的更新可能会在删除之间发生,但每个 Remove() 调用都可以。根据应用程序的行为,您可能会出现竞争,其中具有该键的对象被另一个线程替换,并且根据应用程序的需要,删除更新的键/值对可能不正确。但这与 OP 遇到的问题不同。
【解决方案2】:

您需要在删除以及计算要删除的内容时锁定。移动这个,

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

进入您的锁定部分。

【讨论】:

    【解决方案3】:

    为什么第二个 foreach 在锁之外?

    【讨论】:

    • 我不明白为什么它应该在锁内。我唯一担心的是当我枚举哈希表时,没有其他线程可以修改它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多