在并发收集中使用 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
属性 时,您实际上是在遍历快照。如果同时更新词典,您将不会注意到它。
这是我另一个讨论的问题,但是我很好奇在使用并发字典和 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
属性 时,您实际上是在遍历快照。如果同时更新词典,您将不会注意到它。