正在返回IEnumerable<>线程安全的?

时间:2012-04-19 16:33:24

标签: c# .net-3.5 thread-safety

我有一个Visual Studio 2008 C#.NET 3.5项目,我想拥有一个Foo对象的线程安全池。

public class FooPool
{
    private object pool_lock_ = new object();
    private Dictionary<int, Foo> foo_pool_ = new Dictionary<int, Foo>();

    // ...

    public void Add(Foo f)
    {
        lock (pool_lock_)
        {
            foo_pool_.Add(SomeFooDescriminator, f);
        }
    }

    public Foo this[string key]
    {
        get { return foo_pool_[key]; }
        set { lock (pool_lock_) { foo_pool_[key] = value; } }
    }

    public IEnumerable<Foo> Foos
    {
        get
        {
            lock (pool_lock_)
            {
                // is this thread-safe?
                return foo_pool_.Select(x => x.Value);
            }
        }
    }
}

public IEnumerable<Foo> Foos { get; }函数是否是线程安全的?或者,我是否需要克隆结果并返回一个新列表?

5 个答案:

答案 0 :(得分:22)

不,不是。

如果在调用者枚举时另一个线程添加到字典中,则会出错。

相反,您可以这样做:

lock (pool_lock_) {
    return foo_pool.Values.ToList();
}

答案 1 :(得分:19)

  

IEnumerable<Foo> Foos { get; }函数是否是线程安全的?

没有

  

或者,我是否需要克隆结果并返回一个新列表?

不,因为那也不对。给出错误答案的线程安全方法不是很有用。

如果您锁定并制作副本,那么您要返回的内容是过去的快照。可以将集合更改为完全不同锁定释放的时刻。如果你通过复制使这个线程安全,那么你现在正在把一个充满谎言的包交给你的来电者。

当你处理单线程代码时,一个合理的模型就是一切都保持不变,除非你采取具体的措施来改变一件事。这在多线程代码中不是一个合理的模型。在多线程代码中,您应该采取相反的观点:除非您采取特定措施(例如锁定)以确保事物不会发生变化,否则一切都在不断变化。数百纳秒之前发布一系列描述世界状况的Foos有什么好处呢?在这段时间内,整个世界可能会有所不同。

答案 2 :(得分:18)

线程安全。您需要返回ToList()

return foo_pool_.Select(x => x.Value).ToList();

小心延期执行!

实际上,实际代码在锁退出后运行

// Don't do this
lock (pool_lock_)
{
    return foo_pool_.Select(x => x.Value); // This only prepares the statement, does not run it
}

答案 3 :(得分:1)

您可能需要考虑SynchronizedCollection,

SynchronizedCollection类 提供一个线程安全的集合,其中包含泛型参数指定的类型的对象作为元素。

http://msdn.microsoft.com/en-us/library/ms668265.aspx

答案 4 :(得分:0)

如果您锁定每次读取访问权限,您将以非常糟糕的性能结束。在建议使用toList时,你也会每次都分配内存。

如果您使用.NET 4,请使用新线程安全集合中的ConcurrentDictionary类。它们将提供非常快速(无锁)的机制,用于从多个线程访问数据。

http://msdn.microsoft.com/en-us/library/dd997305.aspx

如果您使用的是旧的.NET版本,我建议您使用带有count变量的循环而不是foreach,如果您只添加元素而不删除它们(如示例所示),它将起作用