Is it OK to use a string as a lock object?

后端 未结 4 788
北荒
北荒 2020-11-27 18:47

I need to make a critical section in an area on the basis of a finite set of strings. I want the lock to be shared for the same string instance, (somewhat similar to String.

相关标签:
4条回答
  • 2020-11-27 19:20

    I'd say it's a really bad idea, personally. That isn't what strings are for.

    (Personally I dislike the fact that every object has a monitor in the first place, but that's a slightly different concern.)

    If you want an object which represents a lock which can be shared between different instances, why not create a specific type for that? You can given the lock a name easily enough for diagnostic purposes, but locking is really not the purpose of a string. Something like this:

    public sealed class Lock
    {
        private readonly string name;
    
        public string Name { get { return name; } }
    
        public Lock(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }
            this.name = name;
        }
    }
    

    Given the way that strings are sometimes interned and sometimes not (in a way which can occasionally be difficult to discern by simple inspection), you could easily end up with accidentally shared locks where you didn't intend them.

    0 讨论(0)
  • 2020-11-27 19:31

    Locking on strings can be problematic, because interned strings are essentially global.

    Interned strings are per process, so they are even shared among different AppDomains. Same goes for type objects (so don't lock on typeof(x)) either.

    0 讨论(0)
  • 2020-11-27 19:31

    I had a similar issue not long ago where I was looking for a good way to lock a section of code based on a string value. Here's what we have in place at the moment, that solves the problem of interned strings and has the granularity we want.

    The main idea is to maintain a static ConcurrentDictionary of sync objects with a string key. When a thread enters the method, it immediately establishes a lock and attempts to add the sync object to the concurrent dictionary. If we can add to the concurrent dictionary, it means that no other threads have a lock based on our string key and we can continue our work. Otherwise, we'll use the sync object from the concurrent dictionary to establish a second lock, which will wait for the running thread to finish processing. When the second lock is released, we can attempt to add the current thread's sync object to the dictionary again.

    One word of caution: the threads aren't queued- so if multiple threads with the same string key are competing simultaneously for a lock, there are no guarantees about the order in which they will be processed.

    Feel free to critique if you think I've overlooked something.

    public class Foo
    {
        private static ConcurrentDictionary<string, object> _lockDictionary = new ConcurrentDictionary<string, object>();
    
        public void DoSomethingThreadCriticalByString(string lockString)
        {
            object thisThreadSyncObject = new object();
    
            lock (thisThreadSyncObject)
            {
                try
                {
                    for (; ; )
                    {
                       object runningThreadSyncObject = _lockDictionary.GetOrAdd(lockString, thisThreadSyncObject);
                       if (runningThreadSyncObject == thisThreadSyncObject)
                           break;
    
                        lock (runningThreadSyncObject)
                        {
                            // Wait for the currently processing thread to finish and try inserting into the dictionary again.
                        }
                    }
    
                    // Do your work here.
    
                }
                finally
                {
                    // Remove the key from the lock dictionary
                    object dummy;
                    _lockDictionary.TryRemove(lockString, out dummy);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 19:34

    Locking on strings is discouraged, the main reason is that (because of string-interning) some other code could lock on the same string instance without you knowing this. Creating a potential for deadlock situations.

    Now this is probably a far fetched scenario in most concrete situations. It's more a general rule for libraries.

    But on the other hand, what is the perceived benefit of strings?

    So, point for point:

    Are there any problems with this approach?

    Yes, but mostly theoretical.

    Is it OK to lock on a string object in this way, and are there any thread safety issues in using the HashSet?

    The HashSet<> is not involved in the thread-safety as long as the threads only read concurrently.

    Is it better to, for example, create a Dictionary that creates a new lock object for each string instance?

    Yes. Just to be on the safe side. In a large system the main aim for avoiding deadlock is to keep the lock-objects as local and private as possible. Only a limited amount of code should be able to access them.

    0 讨论(0)
提交回复
热议问题