9

I am trying to Increment an element in a list in C#, but I need it to be thread safe, so the count does not get affected.

I know you can do this for integers:

Interlocked.Increment(ref sdmpobjectlist1Count);

but this does not work on a list I have the following so far:

lock (padlock)
{
     DifferenceList[diff[d].PropertyName] = DifferenceList[diff[d].PropertyName] + 1;
}

I know this works, but I'm not sure if there is another way to do this?

jg943
  • 309
  • 3
  • 21

3 Answers3

1

As David Heffernan said, ConcurrentDictionary should provider better performance. But, the performance gain might be negligible depending upon how frequently multiple threads try to access the cache.

using System;
using System.Collections.Concurrent;
using System.Threading;

namespace ConcurrentCollections
{
    class Program
    {
        static void Main()
        {
            var cache = new ConcurrentDictionary<string, int>();

            for (int threadId = 0; threadId < 2; threadId++)
            {
                new Thread(
                    () =>
                    {
                        while (true)
                        {
                            var newValue = cache.AddOrUpdate("key", 0, (key, value) => value + 1);
                            Console.WriteLine("Thread {0} incremented value to {1}",
                                Thread.CurrentThread.ManagedThreadId, newValue);
                        }

                    }).Start();
            }

            Thread.Sleep(TimeSpan.FromMinutes(2));
        }
    }
}
Faisal Mansoor
  • 2,041
  • 1
  • 21
  • 25
  • 1
    The update function is not atomic on an AddOrUpdate call. https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx – Ryan Jun 03 '15 at 17:01
  • That comment merely means 'the code might run more than once before it succeeds'. It runs outside the lock, basically. So it gets the existing value, adds 1 to it, takes out a lock and then tries to write it back. If the existing value has changed in the meantime, it repeats the process until it succeeds. – mjwills Jun 19 '17 at 23:18
1

If you use a List<int[]> rather than a List<int>, and have each element in the list be a single-item array, you will be able to do Increment(ref List[whatever][0]) and have it be atomic. One could improve storage efficiency slightly if one defined

class ExposedFieldHolder<T> {public T Value;}

and then used a List<ExposedFieldHolder<int>> and used the statement Increment(ref List[whatever].Value) to perform the increment. Things could be more efficient yet if the built-in types provided a means of exposing an item as a ref or allowed derived classes sufficient access to their internals to provide such ability themselves. They don't, however, so one must either define one's own collection types from scratch or encapsulate each item in its own class object [using an array or a wrapper class].

supercat
  • 77,689
  • 9
  • 166
  • 211
  • Unless you have some very specific reason for writing your own thread safe collection in most cases you'll be far better off with one provided with the TPL. – Ade Miller Apr 08 '13 at 22:49
-1

check the variable you locked on "padLock", normally, you can define it as private static Object padLock = new Object(). if you do not define it as static, each object has its own copy, thus it will not work.

Russel Yang
  • 2,633
  • 3
  • 21
  • 18
  • 5
    It depends on the data. If the data isn't static then there's no need for the lock object to be static; you'd be needlessly synchronizing with threads that aren't trying to access the same data. – Servy Apr 08 '13 at 20:36