2

我有一段这样的代码:

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;
    }
}

我正在锁定对字典的访问,但是我团队中的某个人告诉我它仍然不是线程安全的(没有解释)。问题是 - 你认为这完全是线程安全的吗?

编辑:忘了问,解决方案是什么样的。请注意,我不想锁定整个方法,因为检索用户是一项耗时的操作。

4

4 回答 4

9

不,它不是线程安全的。想象一下,它使用相同的 ID 同时调用了两次,而这在以前是不存在的。

两个线程都会到达RetrieveUser,并且他们都会调用_users.Add(id, u). 第二次调用将失败,因为该键已经存在于字典中。

(顺便说一句,为了可读性,我强烈建议对锁、if 语句等使用大括号。)

于 2013-09-24T22:00:16.600 回答
2

它是线程安全的,它不会破坏任何数据结构。在整个方法以原子方式运行的意义上,它不是线程安全的。两个线程可能会发现该项目丢失,然后创建它然后添加它。其中一个加法器将失败。

于 2013-09-24T22:00:42.407 回答
0

我认为您在这里没有更多选择,因为 _users.Add(id,u) 的结果取决于 RetrieveUser 您必须锁定所有方法以使其线程安全。可能是 john skeet 可以确认这一点
解决方案可能看起来像这样

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;
}

}

希望这有帮助

于 2013-09-24T22:17:20.760 回答
-3

我认为您必须应用单例模式才能拥有真正的线程安全代码。此时,您可以拥有 2 个类的实例。

于 2013-09-24T22:03:15.483 回答