Laden...

ThreadSafeList<T>

Erstellt von inflames2k vor 13 Jahren Letzter Beitrag vor 13 Jahren 5.418 Views
inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren
ThreadSafeList<T>

Beschreibung:

Einige haben es vielleicht mitbekommen, dass ich heute ins Fettnäppchen getreten bin. Nun aber die vollständige Implementation der ThreadSafeList<T>.

Funktionsweise ist identisch zur List<T> des Frameworks, mit dem Unterschied, dass man sich von außen nicht mehr um die Threadsicherheit kümmern muss.

Getestet habe ich die Klasse indem ich 100 Threads in diese schreiben lies und die identische Anzahl an Threads zum lesen aus der Klasse nutzte. Dabei habe ich jeweils 5000 mal schreiben und lesen lassen.

Ich möchte mich nun ersteinmal beim ganzen Forum für meinen vermeidbaren Fehler entschuldigen.

Und zu guter letzt geht noch ein Dank an alle die mir trotz des Fehltritts Rat gaben bei der Implementation der Liste, insbesondere bei Zommi, da er mir die größten Fehler aufzeigte und obwohl ich es nicht explizit wünschte verbesserten Code gepostet hat.

Nun aber zur Klasse:


public class ThreadSafeList<T> : IList<T>
    {
        T[] _innerArray;
        private Object _oLock = new Object();

        public T this[int index]
        {
            get
            {
                if (index >= _count)
                    throw new IndexOutOfRangeException();

                return _innerArray[index];
            }
            set
            {
                if (index >= _count)
                    throw new IndexOutOfRangeException();

                _innerArray[index] = value;
            }
        }

        private int _count;
        /// <summary>
        /// gets the number of listentries
        /// </summary>
        public int Count
        {
            get { return _count; }
        }

        /// <summary>
        /// gets the capacity of the list
        /// </summary>
        public int Capacity
        {
            get { return _innerArray.Length; }
        }

        /// <summary>
        /// creates a new list of t
        /// </summary>
        public ThreadSafeList()
            : this(4)
        {
        }


        /// <summary>
        /// creates a new list of t with the given size
        /// </summary>
        /// <param name="capacity">the size the list should have</param>
        public ThreadSafeList(int capacity)
        {
            _innerArray = new T[capacity];
        }

        /// <summary>
        /// creates a new list of t and copies the entries of a given collection to it
        /// </summary>
        /// <param name="collection">the collection to add</param>
        public ThreadSafeList(IEnumerable<T> collection)
            : this()
        {
            this.AddRange(collection as ICollection<T>);
        }

        /// <summary>
        /// Method to add an item
        /// </summary>
        /// <param name="item">the item to add</param>
        public void Add(T item)
        {
            Insert(_count, item);
        }

        /// <summary>
        /// Method to add a range of items
        /// </summary>
        /// <param name="collection">the collection to add</param>
        public void AddRange(ICollection<T> collection)
        {
            foreach (T item in collection)
                this.Add(item);
        }

        /// <summary>
        /// Method to remove an item
        /// </summary>
        /// <param name="item">the item to remove</param>
        public bool Remove(T item)
        {
            lock(_oLock)
            {
                int index = this.IndexOf(item);
                if (index == -1)
                    return false;

                this.RemoveAt(index);
            }
            return true;
        }

        /// <summary>
        /// Method to remove an item at a specific position
        /// </summary>
        /// <param name="position">the position of the item</param>
        /// <returns>true if the item removed successfull</returns>
        public void RemoveAt(int index)
        {
           lock (_oLock)
           {
                 if (index < 0 || index >= _count) // check range correctly!
                     throw new ArgumentOutOfRangeException();
                 Array.Copy(_innerArray, index + 1, _innerArray, index, _count - index - 1);

                _count--;
            }
        }

        /// <summary>
        /// Method to get the index of an item
        /// </summary>
        /// <param name="item">the item</param>
        /// <returns>the index of the item</returns>
        public int IndexOf(T item)
        {
            lock (_oLock)
            {
                for (int i = 0; i < _count; i++)
                {
                    if (_innerArray[i].Equals(item))
                    {
                        return i;
                    }
                }
            }
            return -1;
        }

        /// <summary>
        /// Method to insert an element at a specific position
        /// </summary>
        /// <param name="index">the index where to insert</param>
        /// <param name="item">the item to insert</param>
        public void Insert(int index, T item)
        {
            lock (_oLock)
            {
                if (index < 0 || index > _count) // check range correctly!
                    throw new ArgumentOutOfRangeException();

                T[] oldArray = _innerArray;
                T[] newArray = _innerArray;

                if (_count == this.Capacity) // optionally expand array and copy first half
                {
                    newArray = new T[_count * 2];
                    Array.Copy(oldArray, 0, newArray, 0, index); // copy elements before index
                }
                Array.Copy(oldArray, index, newArray, index + 1, _count - index); // shift elements after index

                _innerArray = newArray;

                _innerArray[index] = item;

                _count++;
            }
        }

        /// <summary>
        /// Method to clear the collection
        /// </summary>
        public void Clear()
        {
            lock (_oLock)
            {
                _innerArray = new T[4];
                _count = 0;
            }
        }

        /// <summary>
        /// Method to check if an item is inside the list
        /// </summary>
        /// <param name="item">the item to check</param>
        /// <returns>true if the item is in the list</returns>
        public bool Contains(T item)
        {
            return IndexOf(item) != -1;
        }

        /// <summary>
        /// Method to copy parts of the list to an aray
        /// </summary>
        /// <param name="array">the array to copy to</param>
        /// <param name="arrayIndex">the startindex</param>
        public void CopyTo(T[] array, int arrayIndex)
        {
            lock(_oLock)
            { 
                if (arrayIndex < 0 || arrayIndex > _count)
                    throw new IndexOutOfRangeException();

                Array.Copy(_innerArray, arrayIndex, array, 0, _count - arrayIndex);
            }
        }

        /// <summary>
        /// Method to check if the list is read only
        /// </summary>
        public bool IsReadOnly
        {
            get { return false; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            lock(_oLock)
            {
                 for (int i = 0; i < _count; i++)
                 {
                     yield return _innerArray[i];
                 }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

Schlagwörter: Collection, List, ThreadSafeList

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo inflames2k,

ThreadSafeList!
Eine Threadspeicherliste hast du ja nicht erstellt 😉

Aber fehlerfrei ist deine Implementierung immer noch nicht.
Sowohl deine Rangechecks als auch der Enumerator können bei konkurrierenden Zugriffen noch zu Problemen führen!

inflames2k Themenstarter:in
2.298 Beiträge seit 2010
vor 13 Jahren

Danke, stimmt. Hab ich geändert.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

Gelöschter Account
vor 13 Jahren

Wenn du schon bei AddRange das Interface ICollection hast, dann solltest du auch dessen Count Eigenschaft verwenden um die Größe mit einem male anzupassen anstatt wie bis jetzt immer häppchenweise.

Mit dem Lock(this) wäre ich eher zurückhaltend. Üblicherweise nimmt man lieber, ein privates Lockobjekt, da es bei gewissen Konstellationen zu Deadlocks kommen kann wenn man this verwendet.

Ansonsten sieht es recht ordentlich aus 👍

Hinweis von MarsStein vor 13 Jahren

Um einer neuen Diskussion darüber vorzugreifen: Das lock(this)-Thema ist bereits erschöpfend in Sollte man lock this vermeiden? behandelt.