简体   繁体   中英

C# usage of lock() statements and caching of data

I have a number of static List's in my application, which are used to store data from my database and are used when looking up information:

public static IList<string> Names;

I also have some methods to refresh this data from the database:

public static void GetNames()
{
    SQLEngine sql = new SQLEngine(ConnectionString);
    lock (Names)
    {
        Names = sql.GetDataTable("SELECT * FROM Names").ToList<string>();
    }
}

I initially didnt have the lock() in place, however i noticed very occasionally, the requesting thread couldnt find the information in the list. Now, I am assuming that if the requesting thread tries to access the Names list, it cant until it has been fully updated.

Is this the correct methodology and usage of the lock() statement?

As a sidenote, i noticed on MSDN that one shouldnt use lock() on public variables. Could someone please elaborate in my particular scenario?

No, it's not correct since anyone can use the Names property directly.

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

Try to avoid static methods. At least use a singleton.

The check, lock, check is faster than a lock, check since the write will only occur once.

Assigning a property on usage is called lazy loading.

The _namesLock is required since you can't lock on null.

lock is only useful if all places intended to be synchronized also apply the lock. So every time you access Names you would be required to lock . At the moment, that only stops 2 threads swapping Names at the same time, which frankly isn't a problem here anyway, as reference swaps are atomic anyway.

Another problem; presumably Names starts off null ? You can't lock a null . Equally, you shouldn't lock on something that may change reference . If you want to synchronize, a common approach is something like:

// do not use for your scenario - see below
private static readonly object lockObj = new object();

then lock(lockObj) instead of your data.

With regards to not locking things that are visible externally; yes. That is because some other code could randomly choose to lock on it, which could cause unexpected blocking, and quite possibly deadlocks.

The other big risk is that some of your code obtains the names, and then does a sort/add/remove/clear/etc - anything that mutates the data. Personally, I would be using a read-only list here. In fact, with a read-only list, all you have is a reference swap; since that is atomic, you don't need any locking:

public static IList<string> Names { get; private set; }
public static void UpdateNames() {
    List<string> tmp = SomeSqlQuery();
    Names = tmp.AsReadOnly();
}

And finally: public fields are very very rarely a good idea. Hence the property above. This will be inlined by the JIT, so it is not a penalty.

From the oode you have shown, the first time GetNames() is called the Names property is null. I don't known what a lock on a null object would do. I would add a variable to lock on.

 static object namesLock = new object();

Then in GetNames()

 lock (namesLock)
 {
      if (Names == null)
        Names = ...;
 }

We do the if test inside of the lock() to stop race conditions. I'm assuming that the caller of GetNames() also does the same test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM