在并发收集中使用 yield

Using yield with concurrent collection

这是我另一个讨论的问题,但是我很好奇在使用并发字典和 yield 功能时在以下情况下会发生什么。

IEnumerable<int> GetValuesNotZero()
{
    foreach(int value in dictionary.Values)
    {
        if(value != 0)
        {
            yield return value;
        }
    }
}

如果字典中的另一个线程 adds/delete/update,foreach 循环中的值会发生什么情况?当我迭代字典时,字典是否被锁定,或者我是否仍然 运行 丢失 added/deleted/updated 值的风险?

当你迭代时,如果另一个线程修改了字典,你会发现更新的值而不是异常(正常 Dictionary<,>)。

如果您还没有到达修改的项目,您最终会完成它;否则,如果您已经访问了更新的元素,您可能会错过更新的值。

来自ConcurrentDictionary.GetEnumerator

The enumerator returned from the dictionary is safe to use concurrently with reads and writes to the dictionary, however it does not represent a moment-in-time snapshot of the dictionary. The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called.

例如,假设线程 1 正在遍历字典,现在位于位置 3。如果线程 2 修改了位置 7 的元素,您将找到修改后的值。另一方面,如果它修改了位置 1 的元素,您将不会注意到它。 (忘记字典是无序的事实;使用位置是为了更好地理解)。


我刚刚意识到你的代码循环通过 Dictionary.Values(感谢@Svick 在评论中提到);仅当您遍历字典时,上面的答案才正确。当您遍历 Values 属性 时,您实际上是在遍历快照。如果同时更新词典,您将不会注意到它。