C#使用lock()语句和缓存数据

时间:2012-02-16 07:23:46

标签: c# multithreading locking

我的应用程序中有许多静态列表,用于存储数据库中的数据,并在查找信息时使用:

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()。有人可以在我的特定场景中详细说明吗?

3 个答案:

答案 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,这无论如何都不是问题,因为参考交换无论如何都是原子的。

另一个问题;大概是Namesnull开始?你不能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()的调用者也进行相同的测试。