C#中的收益率是否是线程安全的?

Alb*_*bic 26 c# ienumerable yield yield-return

我有以下代码:

private Dictionary<object, object> items = new Dictionary<object, object>;
public IEnumerable<object> Keys
{
    get
    {
        foreach (object key in items.Keys)
        {
            yield return key;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是线程安全的吗?如果不是我必须lock围绕循环或yield return

这就是我的意思:

KeysThread1 访问属性,而Thread2将一个项添加到基础字典.Thread1是否受Thread2的影响?

Jon*_*eet 20

什么是线程安全的意思?

当你在迭代它时,你当然不应该更改字典,无论是否在同一个线程中.

如果通常在多个线程中访问字典,则调用者应取出一个锁(覆盖所有访问的同一个锁),以便它们可以在迭代结果的过程中锁定.

编辑:要响应您的编辑,它不会与锁定代码对应.迭代器块没有自动取出锁定 - 它怎么会知道syncRoot呢?

而且,只是锁定返回IEnumerable<TKey>也不会使它成为线程安全的 - 因为锁只会影响它返回序列的时间段,而不会影响它被迭代的时间段.


Jus*_*ner 18

查看这篇文章,了解幕后发生的yield关键字:

在C#yield关键字的幕后

简而言之 - 编译器接受您的yield关键字并在IL中生成一个完整的类来支持该功能.您可以在跳转后查看页面并查看生成的代码...并且该代码看起来像跟踪线程ID以保证安全.


Alb*_*bic 10

好的,我做了一些测试,得到了一个有趣的结果.

它似乎更多是底层集合的枚举器而不是yield关键字的问题.枚举器(实际上是它的MoveNext方法)抛出(如果正确实现),InvalidOperationException因为枚举已更改.根据MoveNext方法MSDN文档,这是预期的行为.

因为通过集合枚举通常不是线程安全的,yield return所以也不是.