Search code examples
c#interlocked

How to update my model counter value using Interlocked.Add


So I have this model:

public class Container : INotifyPropertyChanged
{
    private int _total;
    private static InjectionContainer _mainContainer = new InjectionContainer();
    private static InjectionContainer _secondContainer = new InjectionContainer();
    private ObservableCollection<MyData> _files = new ObservableCollection<MyData>();

    public int TotalPackets
    {
        get { return _total; }
        set
        {
            _total = value;
            OnPropertyChanged("Total");
        }
    }

    public ObservableCollection<MyData> List
    {
        get { return _files; }
        set { _files = value; }
    }
}

And outside of this Container class I want to update my class Total property but I need it to be thread safe cause many thread do it at the same time:

public static void UpdateTotal(Container container, int value)
{
    Interlocked.Add(ref container.Total, value);
}

And got this error:

A property or indexer may not be passed as an out or ref parameter


Solution

  • You should create an Add method inside the Container:

    public class Container : INotifyPropertyChanged
    {
        private int _total;
        private static InjectionContainer _mainContainer = new InjectionContainer();
        private static InjectionContainer _secondContainer = new InjectionContainer();
        private ObservableCollection<MyData> _files = new ObservableCollection<MyData>();
    
        public int TotalPackets
        {
            get { return _total; }
        }
    
        public ObservableCollection<MyData> List
        {
            get { return _files; }
            set { _files = value; }
        }
    
        public void AddTotal(int value)
        {
            Interlocked.Add(ref _total, value);
            OnPropertyChanged("TotalPackets");
        }
    }
    

    You can't add the Interlocked.Add(ref _total, value); into the setter because the needed usage pattern would then still be non-thread-save:

    var total = container.TotalPackets; // #1
    total += 10; // #2
    container.TotalPackets = total; // #3
    

    There the setting of the new total value in #3 on its own would be thread-safe, but between #1 and #3 some other thread could already have changed the total value. If we think of two threads and a start total of 10 the following execution order could happen:

    1. Thread 1 - #1 ==> read 10
    2. Thread 1 - #2 ==> total is set to 20
    3. Thread 2 - #1 ==> read 10, as thread 1 has not run #3 yet
    4. Thread 1 - #3 ==> TotalPackets is set to 20 (initial 10 + 10 from #2/2.)
    5. Thread 2 - #2 ==> total is set to 20 as at 3. it was still 10
    6. Thread 2 - #3 ==> TotalPackets is set to 20 again => boom ;-)