Threadsafe collection without lock

后端 未结 5 2027
一个人的身影
一个人的身影 2020-12-13 00:59

I am preparing myself for an interview and I came across the followign question. I tried but I could not find anything which can create a class containing thread safe collec

相关标签:
5条回答
  • 2020-12-13 01:34

    Based on the question you should be able to add a concurrent collection inside your object that will handle the thread safety requirements for you. They did not specify what type of internal collection to use.

    You should be able to implement one of the collections from the concurrentcollection namespace and achieve this.

    http://msdn.microsoft.com/en-us/library/system.collections.concurrent.aspx

    0 讨论(0)
  • 2020-12-13 01:43

    You could create a non blocking linked list. For example something like this.

    The .net framework provides methods like CompareExchange(Object, Object, Object) that allow you to write safe code without locking the whole list.

    0 讨论(0)
  • 2020-12-13 01:50

    My solution. Basically mimic locking using Interlocked.Exchange and an AutoResetEvents. Did some simple tests and it seems working.

        public class SharedStringClass
        {
            private static readonly int TRUE = 1;
            private static readonly int FALSE = 0;
    
            private static int allowEntry;
    
            private static AutoResetEvent autoResetEvent;
    
            private IList<string> internalCollection;
    
            public SharedStringClass()
            {
                internalCollection = new List<string>();
                autoResetEvent = new AutoResetEvent(false);
                allowEntry = TRUE;
            }
    
            public void AddString(string strToAdd)
            {
                CheckAllowEntry();
    
                internalCollection.Add(strToAdd);
    
                // set allowEntry to TRUE atomically
                Interlocked.Exchange(ref allowEntry, TRUE);
                autoResetEvent.Set();
            }
    
            public string ToString()
            {
                CheckAllowEntry();
    
                // access the shared resource
                string result = string.Join(",", internalCollection);
    
                // set allowEntry to TRUE atomically
                Interlocked.Exchange(ref allowEntry, TRUE);
                autoResetEvent.Set();
                return result;
            }
    
            private void CheckAllowEntry()
            {
                while (true)
                {
                    // compare allowEntry with TRUE, if it is, set it to FALSE (these are done atomically!!)
                    if (Interlocked.CompareExchange(ref allowEntry, FALSE, TRUE) == FALSE)
                    {
                        autoResetEvent.WaitOne();
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-13 01:53

    The easiest solution is having a field of type string[]. Whenever a caller wants to add a string, create a new array with the new item appended and swap it for the old one.

    This model does not require synchronization. It does not tolerate concurrent writers but it allows for concurrent reading.

    0 讨论(0)
  • 2020-12-13 01:56

    Here’s a way of achieving lock-free modification of a collection by working on a local copy and then attempting to atomically swap it with the global collection whilst checking for races:

    public class NonLockingCollection
    {
        private List<string> collection;
    
        public NonLockingCollection()
        {
            // Initialize global collection through a volatile write.
            Interlocked.CompareExchange(ref collection, new List<string>(), null);
        }
    
        public void AddString(string s)
        {
            while (true)
            {
                // Volatile read of global collection.
                var original = Interlocked.CompareExchange(ref collection, null, null);
    
                // Add new string to a local copy.
                var copy = original.ToList();
                copy.Add(s);
    
                // Swap local copy with global collection,
                // unless outraced by another thread.
                var result = Interlocked.CompareExchange(ref collection, copy, original);
                if (result == original)
                    break;
            }
        }
    
        public override string ToString()
        {
            // Volatile read of global collection.
            var original = Interlocked.CompareExchange(ref collection, null, null);
    
            // Since content of global collection will never be modified,
            // we may read it directly.
            return string.Join(",", original);
        }
    }
    

    Edit: Since using Interlocked.CompareExchange to implicitly perform volatile reads and writes has given rise to some confusion, I’m posting below the equivalent code with Thread.MemoryBarrier calls instead.

    public class NonLockingCollection
    {
        private List<string> collection;
    
        public NonLockingCollection()
        {
            // Initialize global collection through a volatile write.
            collection = new List<string>();
            Thread.MemoryBarrier();
        }
    
        public void AddString(string s)
        {
            while (true)
            {
                // Fresh volatile read of global collection.
                Thread.MemoryBarrier();
                var original = collection;
                Thread.MemoryBarrier();
    
                // Add new string to a local copy.
                var copy = original.ToList();
                copy.Add(s);
    
                // Swap local copy with global collection,
                // unless outraced by another thread.
                var result = Interlocked.CompareExchange(ref collection, copy, original);
                if (result == original)
                    break;
            }
        }
    
        public override string ToString()
        {
            // Fresh volatile read of global collection.
            Thread.MemoryBarrier();
            var original = collection;
            Thread.MemoryBarrier();
    
            // Since content of global collection will never be modified,
            // we may read it directly.
            return string.Join(",", original);
        }
    }
    
    0 讨论(0)
提交回复
热议问题