我的应用程序中有许多静态列表,用于存储数据库中的数据,并在查找信息时使用:
public static IList<string> Names;
我也有一些方法可以从数据库中刷新这些数据:
public static void GetNames()
{
SQLEngine sql = new SQLEngine(ConnectionString);
lock (Names)
{
Names = sql.GetDataTable("SELECT * FROM Names").ToList<string>();
}
}
我最初没有使用lock(),但是我偶尔注意到,请求线程无法在列表中找到信息。现在,我假设如果请求线程尝试访问名称列表,则它不能完全更新。
这是lock()语句的正确方法和用法吗?
作为旁注,我注意到在MSDN上不应该对公共变量使用lock()。有人可以在我的特定场景中详细说明吗?
答案 0 :(得分:3)
不,这是不对的,因为任何人都可以直接使用Names属性。
public class SomeClass
{
private List<string> _names;
private object _namesLock = new object();
public IEnumerable<string> Names
{
get
{
if (_names == null)
{
lock (_namesLock )
{
if (_names == null)
_names = GetNames();
}
}
return _names;
}
}
public void UpdateNames()
{
lock (_namesLock)
GetNames();
}
private void GetNames()
{
SQLEngine sql = new SQLEngine(ConnectionString);
_names = sql.GetDataTable("SELECT * FROM Names").ToList<string>();
}
}
尽量避免使用静态方法。至少使用单身人士。
检查,锁定,检查比锁定更快,检查,因为写入只会发生一次。
在使用时分配属性称为延迟加载。
_namesLock
是必需的,因为您无法锁定null。
答案 1 :(得分:3)
lock
仅在所有地点打算同步同时应用锁定时才有用。因此,您访问Names
的每个时间都需要lock
。目前,这只会阻止两个线程同时交换Names
,这无论如何都不是问题,因为参考交换无论如何都是原子的。
另一个问题;大概是Names
从null
开始?你不能lock
一个null
。同样,您不应该锁定可能更改参考的内容。如果要进行同步,常见的方法如下:
// do not use for your scenario - see below
private static readonly object lockObj = new object();
然后lock(lockObj)
而不是您的数据。
关于不锁定外部可见的东西;是。这是因为其他一些代码可能会随机选择lock
,这可能会导致意外阻塞,并且很可能会出现死锁。
另一个很大的风险是你的一些代码获得名称,然后进行排序/添加/删除/清除/等等 - 任何改变数据的东西。就个人而言,我会在这里使用只读列表。实际上,使用只读列表,您只需要一个引用交换;因为那是原子的,你不需要需要任何锁定:
public static IList<string> Names { get; private set; }
public static void UpdateNames() {
List<string> tmp = SomeSqlQuery();
Names = tmp.AsReadOnly();
}
最后:公共领域非常非常很少是一个好主意。因此上面的财产。这将由JIT内联,因此不是惩罚。
答案 2 :(得分:0)
从您显示的oode中,第一次调用GetNames()时,Names属性为null。我不知道null对象上的锁会做什么。我会添加一个变量来锁定。
static object namesLock = new object();
然后在GetNames()
中 lock (namesLock)
{
if (Names == null)
Names = ...;
}
我们在lock()内部进行if测试以停止竞争条件。我假设GetNames()的调用者也进行相同的测试。