这是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];
}
}
答案 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(...)
就会锁定该对象。因此,当下一个线程到达此行时,他无法锁定该对象并等待另一个线程释放锁定。