是否返回IEnumerable 线程安全?
我有一个Visual Studio 2008 C#.NET 3.5项目,我希望有一个线程安全的Foo
对象池。
public class FooPool { private object pool_lock_ = new object(); private Dictionary foo_pool_ = new Dictionary(); // ... 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 Foos { get { lock (pool_lock_) { // is this thread-safe? return foo_pool_.Select(x => x.Value); } } } }
public IEnumerable Foos { get; }
函数线程安全吗? 或者,我是否需要克隆结果并返回新列表?
不,不是。
如果在调用者枚举时另一个线程添加到字典中,则会出现错误。
相反,你可以这样做:
lock (pool_lock_) { return foo_pool.Values.ToList(); }
是
IEnumerable
函数线程安全吗?Foos { get; }
没有。
或者,我是否需要克隆结果并返回新列表?
不,因为那也不对。 给出错误答案的线程安全方法不是很有用。
如果您锁定并制作副本,那么您要返回的内容就是过去的快照 。 在释放锁定时 ,可以将集合更改为完全不同。 如果你通过复制使这个线程安全,那么你现在正在把一个充满谎言的包交给你的来电者。
当你处理单线程代码时,一个合理的模型就是一切都保持不变,除非你采取具体的措施来改变一件事。 这在multithreading代码中不是一个合理的模型。 在multithreading代码中,您应该采取相反的观点:除非您采取特定措施(例如锁定)以确保事情不会发生变化,否则一切都在不断变化。 数百纳秒之前发布一系列描述世界遥远的世界状态的Foos有什么好处? 在这段时间内,整个世界可能会有所不同。
它不是线程安全的。 你需要返回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 }
你可能想要考虑一个SynchronizedCollection,
SynchronizedCollection类提供线程安全的集合,其中包含generics参数指定的类型的对象作为元素。
如果您锁定每次读取访问权限,您将以非常糟糕的性能结束。 并且在使用toList的建议中,您还将每次分配内存。
如果您使用.NET 4,只需使用新线程安全集合中的ConcurrentDictionary类。 它们将提供非常快速(无锁)的机制,用于从多个线程访问数据。
http://msdn.microsoft.com/en-us/library/dd997305.aspx
如果您使用的是旧的.NET版本,我建议您使用带有count变量的循环而不是foreach,如果您只添加元素而不删除它们,它将起作用(如您的示例中所示)