How to lock on an integer in C#?

后端 未结 14 2125
醉梦人生
醉梦人生 2020-12-05 14:44

Is there any way to lock on an integer in C#? Integers can not be used with lock because they are boxed (and lock only locks on references).

The scenario is as follo

相关标签:
14条回答
  • 2020-12-05 15:01

    You should use a sync object like this:

    public class YourForm
    {
        private static object syncObject = new object();
    
        public void Moderate()
        {
            lock(syncObject)
            {
                // do your business
            }
        }
    }
    

    But this approach shouldn't be used in a web app scenario.

    0 讨论(0)
  • 2020-12-05 15:02

    This option builds on the good answer provided by configurator with the following modifications:

    1. Prevents the size of the dictionary from growing uncontrollably. Since, new posts will get new ids, your dictionary of locks will grow indefinitely. The solution is to mod the id against a maximum dictionary size. This does mean that some ids will have the same lock (and have to wait when they would otherwise not have to), but this will be acceptable for some dictionary size.
    2. Uses ConcurrentDictionary so there is no need for a separate dictionary lock.

    The code:

    internal class IdLock
    {
        internal int LockDictionarySize
        {
            get { return m_lockDictionarySize; }
        }
        const int m_lockDictionarySize = 1000;
        ConcurrentDictionary<int, object> m_locks = new ConcurrentDictionary<int, object>();
        internal object this[ int id ]
        {
            get
            {
                object lockObject = new object();
                int mapValue = id % m_lockDictionarySize;
                lockObject = m_locks.GetOrAdd( mapValue, lockObject );
                return lockObject;
            }
        }
    }
    

    Also, just for completeness, there is the alternative of string interning: -

    1. Mod the id against the maximum number of interned id strings you will allow.
    2. Convert this modded value to a string.
    3. Concatenate the modded string with a GUID or namespace name for name collision safety.
    4. Intern this string.
    5. lock on the interned string. See this answer for some information:

    The only benefit of the string interning approach is that you don't need to manage a dictionary. I prefer the dictionary of locks approach as the intern approach makes a lot of assumptions about how string interning works and that it will continue to work in this way. It also uses interning for something it was never meant / designed to do.

    0 讨论(0)
  • 2020-12-05 15:02

    I doubt you should use a database or O/S level feature such as locks for a business level decision. Locks incur significant overheads when held for long times (and in these contexts, anything beyond a couple of hundred milliseconds is an eternity).

    Add a status field to the post. If you deal with several therads directly, then you can use O/S level locks -- to set the flag.

    0 讨论(0)
  • 2020-12-05 15:04

    Coresystem at codeplex has two class for thread synchronization based on value types, for details see http://codestand.feedbook.org/2012/06/lock-on-integer-in-c.html

    0 讨论(0)
  • 2020-12-05 15:05

    Ideally you can avoid all the complex and brittle C# locking and replace it with database locking, if your transactions are designed correctly then you should be able to get by with DB transactions only.

    0 讨论(0)
  • 2020-12-05 15:08

    I've read a lot of comments mentioning that locking isn't safe for web applications, but, other than web farms, I haven't seen any explanations of why. I would be interested in hearing the arguments against it.

    I have a similar need, though I'm caching re-sized images on the hard drive (which is obviously a local action so a web farm scenario isn't an issue).

    Here is a redone version of what @Configurator posted. It includes a couple features that @Configurator didn't include:

    1. Unlocking: Ensures the list doesn't grow unreasonably large (we have millions of photos and we can have many different sizes for each).
    2. Generic: Allows locking based on different data types (such as int or string).

    Here's the code...

    /// <summary>
    /// Provides a way to lock a resource based on a value (such as an ID or path).
    /// </summary>
    public class Synchronizer<T>
    {
    
        private Dictionary<T, SyncLock> mLocks = new Dictionary<T, SyncLock>();
        private object mLock = new object();
    
        /// <summary>
        /// Returns an object that can be used in a lock statement. Ex: lock(MySync.Lock(MyValue)) { ... }
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public SyncLock Lock(T value)
        {
            lock (mLock)
            {
                SyncLock theLock;
                if (mLocks.TryGetValue(value, out theLock))
                    return theLock;
    
                theLock = new SyncLock(value, this);
                mLocks.Add(value, theLock);
                return theLock;
            }
        }
    
        /// <summary>
        /// Unlocks the object. Called from Lock.Dispose.
        /// </summary>
        /// <param name="theLock"></param>
        public void Unlock(SyncLock theLock)
        {
            mLocks.Remove(theLock.Value);
        }
    
        /// <summary>
        /// Represents a lock for the Synchronizer class.
        /// </summary>
        public class SyncLock
            : IDisposable
        {
    
            /// <summary>
            /// This class should only be instantiated from the Synchronizer class.
            /// </summary>
            /// <param name="value"></param>
            /// <param name="sync"></param>
            internal SyncLock(T value, Synchronizer<T> sync)
            {
                Value = value;
                Sync = sync;
            }
    
            /// <summary>
            /// Makes sure the lock is removed.
            /// </summary>
            public void Dispose()
            {
                Sync.Unlock(this);
            }
    
            /// <summary>
            /// Gets the value that this lock is based on.
            /// </summary>
            public T Value { get; private set; }
    
            /// <summary>
            /// Gets the synchronizer this lock was created from.
            /// </summary>
            private Synchronizer<T> Sync { get; set; }
    
        }
    
    }
    

    Here's how you can use it...

    public static readonly Synchronizer<int> sPostSync = new Synchronizer<int>();
    ....
    using(var theLock = sPostSync.Lock(myID))
    lock (theLock)
    {
        ...
    }
    
    0 讨论(0)
提交回复
热议问题