这个静态字典字段是否安全?

时间:2014-04-08 14:18:57

标签: c# asp.net-mvc multithreading

这是Asp.net MVC Web项目。

我每隔30秒准备一次“用户树”。 (RefreshUsers方法)所以set操作是原子的。 获取操作是多线程的。

这种方法线程安全吗?

public static class InternalCache
{
    private static Dictionary<long, UserModel> _Users;

    public static RefreshUsers(){
        //.............
        // var res=.......
        _Users= res;
    }

    public static UserModel GetUser(long id){
        return _Users[id];
    }
}

2 个答案:

答案 0 :(得分:3)

虽然静态变量的设置是原子的,但仍然存在编译器优化和指令重新排序可能导致某些线程看到_Users的陈旧值的风险。

要删除它,您需要在访问静态变量时使用lock或使用Interlocked函数来读取/写入变量:

public static class InternalCache
{
    private static Dictionary<long, UserModel> _Users;


    public static RefreshUsers()
    {
        Interlocked.Exchange(ref _Users, res);
    }

    public static UserModel GetUser(long id)
    {            
        // Read the variable, ensuring we always see the latest value
        var users = Interlocked.CompareExchange(ref _Users, null, null);
        return users[id];            
    }
}

答案 1 :(得分:2)

如果您可以确保一次只有一个线程调用RefreshUsers函数,那么您的代码应该是线程安全的。

否则你必须使函数成为线程安全的:

为了使函数线程安全,您需要一个对象来锁定:

private static object _lockObj = new Object();

然后在您写信_Users时以及获取时锁定该对象:

public static class InternalCache
{
    private static Dictionary<long, UserModel> _Users;
    private static object _lockObj = new Object();

    public static RefreshUsers(){
        lock (_lockObj)
        {
            //.............
            // var res=.......
            _Users= res;
        }
    }

    public static UserModel GetUser(long id){            
        return _Users[id];            
    }
}

锁定确保锁定块内的所有内容一次只能被一个线程访问,因为线程一旦到达行lock(...)就会锁定该对象。因此,当下一个线程到达此行时,他无法锁定该对象并等待另一个线程释放锁定。