Note that there are some explanatory texts on larger screens.

plurals
  1. POMultithreaded Synchronised List<T>
    text
    copied!<p>I have a requirement whereby I needed to store a simple cache of a list of items. I was using List&lt; T > for this purpose, but we have now changed the design to accommodate multiple threads.</p> <p>The architecture of the system is driven by events, therefore it's quite likely that a read and write operation could collide. Since the vast majority of access will be read-only I thought that the ReaderWriterLockSlim might be a good candidate. The cache only needs to be accurate at the point of reading for that moment in time.</p> <p>I have written the code below and it seems to work ok, but are there some potential pain points?</p> <p><strong>UPDATE: Whilst the code below does fix some synchronisation problems it's not 100% perfect. I have since decided to implement a much simpler class that doesn't expose all of the IList&lt; T > operations and therefore makes it 'safer' to re-use.</strong></p> <pre><code>public class SynchronisedList&lt;T&gt; : IList&lt;T&gt; { private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); private IList&lt;T&gt; innerCache = new List&lt;T&gt;(); private U ReadReturn&lt;U&gt;(Func&lt;U&gt; function) { cacheLock.EnterReadLock(); try { return function(); } finally { cacheLock.ExitReadLock(); } } private void Read(Action action) { cacheLock.EnterReadLock(); try { action(); } finally { cacheLock.ExitReadLock(); } } private U WriteReturn&lt;U&gt;(Func&lt;U&gt; function) { cacheLock.EnterWriteLock(); try { return function(); } finally { cacheLock.ExitWriteLock(); } } private void Write(Action action) { cacheLock.EnterWriteLock(); try { action(); } finally { cacheLock.ExitWriteLock(); } } public T this[int index] { get { return ReadReturn(() =&gt; innerCache[index]); } set { Write(() =&gt; innerCache[index] = value); } } public int IndexOf(T item) { return ReadReturn(() =&gt; innerCache.IndexOf(item)); } public void Insert(int index, T item) { Write(() =&gt; innerCache.Insert(index, item)); } public void RemoveAt(int index) { Write(() =&gt; innerCache.RemoveAt(index)); } public void Add(T item) { Write(() =&gt; innerCache.Add(item)); } public void Clear() { Write(() =&gt; innerCache.Clear()); } public bool Contains(T item) { return ReadReturn(() =&gt; innerCache.Contains(item)); } public int Count { get { return ReadReturn(() =&gt; innerCache.Count); } } public bool IsReadOnly { get { return ReadReturn(() =&gt; innerCache.IsReadOnly); } } public void CopyTo(T[] array, int arrayIndex) { Read(() =&gt; innerCache.CopyTo(array, arrayIndex)); } public bool Remove(T item) { return WriteReturn(() =&gt; innerCache.Remove(item)); } public IEnumerator&lt;T&gt; GetEnumerator() { return ReadReturn(() =&gt; innerCache.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { return ReadReturn(() =&gt; (innerCache as IEnumerable).GetEnumerator()); } } internal class Program { private static SynchronisedList&lt;int&gt; list = new SynchronisedList&lt;int&gt;(); private static void Main() { for (int i = 0; i &lt; 500000; i++) { var index = i; ThreadPool.QueueUserWorkItem((state) =&gt; { var threadNum = (int)state; if (threadNum % 10 == 0) { list.Add(threadNum); } else { Console.WriteLine(list.Count); } }, index); } Console.ReadKey(); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload