我有一段这样的代码:
public class UserCache
{
private Dictionary<int, User> _users = new Dictionary<int, User>();
public User GetUser(int id)
{
User u = null;
lock (_users)
{
if (_users.containsKey(id))
return _users[id];
}
//The below line is threadsafe, so no worries on that.
u = RetrieveUser(id); // Method to retrieve from database;
lock (_users)
{
_users.Add(id, u);
}
return u;
}
}
我正在锁定对字典的访问权限,但是我的团队中有人告诉它仍然没有线程安全(没有解释)。问题是 - 你认为这是线程安全的吗?
编辑:忘了询问,解决方案会是什么样子。请注意我没有想要锁定整个方法,因为检索用户是一个耗时的操作。
答案 0 :(得分:9)
不,它不是线程安全的。想象一下,它使用相同的ID同时调用两次,这在以前不存在。
两个线程都会达到RetrieveUser
,并且两个都会调用_users.Add(id, u)
。第二个调用将失败,因为密钥已存在于字典中。
(顺便说一下,为了便于阅读,我强烈建议使用大括号来锁定if语句等。)
答案 1 :(得分:2)
在某种意义上它是线程安全的,它不会破坏任何数据结构。从整个方法原子行为的意义上说,它不是线程安全的。两个线程可能会找到要丢失的项目,然后创建它然后添加它。其中一个加法器会失败。
答案 2 :(得分:0)
我不认为你在这里有更多的选择,因为_users.Add(id,u)的结果依赖于RetrieveUser,你必须锁定所有方法以使其线程安全。可能是约翰双向飞碟可以证实这一点 解决方案可能看起来像这样
public class UserCache
{
private Dictionary<int, User> _users = new Dictionary<int, User>();
private readonly object _syncLock = new object();
public User GetUser(int id)
{
User u = null;
lock (_syncLock)
{
if (_users.containsKey(id))
return _users[id];
//The below line is threadsafe, so no worries on that.
u = RetrieveUser(id); // Method to retrieve from database;
_users.Add(id, u);
}
return u;
}
}
希望这个帮助
答案 3 :(得分:-3)
我认为您必须应用单例模式才能拥有真正的线程安全代码。在这一刻,你可以有2个班级实例。