14

I have property definition in class where i have only Counters, this must be thread-safe and this isn't because get and set is not in same lock, How to do that?

    private int _DoneCounter;
    public int DoneCounter
    {
        get
        {
            return _DoneCounter;
        }
        set
        {
            lock (sync)
            {
                _DoneCounter = value;
            }
        }
    }
Svisstack
  • 16,203
  • 6
  • 66
  • 100

5 Answers5

29

If you're looking to implement the property in such a way that DoneCounter = DoneCounter + 1 is guaranteed not to be subject to race conditions, it can't be done in the property's implementation. That operation is not atomic, it actually three distinct steps:

  1. Retrieve the value of DoneCounter.
  2. Add 1
  3. Store the result in DoneCounter.

You have to guard against the possibility that a context switch could happen in between any of those steps. Locking inside the getter or setter won't help, because that lock's scope exists entirely within one of the steps (either 1 or 3). If you want to make sure all three steps happen together without being interrupted, then your synchronization has to cover all three steps. Which means it has to happen in a context that contains all three of them. That's probably going to end up being code that does not belong to whatever class contains the DoneCounter property.

It is the responsibility of the person using your object to take care of thread safety. In general, no class that has read/write fields or properties can be made "thread-safe" in this manner. However, if you can change the class's interface so that setters aren't necessary, then it is possible to make it more thread-safe. For example, if you know that DoneCounter only increments and decrements, then you could re-implement it like so:

private int _doneCounter;
public int DoneCounter { get { return _doneCounter; } }
public int IncrementDoneCounter() { return Interlocked.Increment(ref _doneCounter); }
public int DecrementDoneCounter() { return Interlocked.Decrement(ref _doneCounter); }
Sean U
  • 6,730
  • 1
  • 24
  • 43
  • I using that counter in way like Counter += X; and X is not 1 ;-) i have counter from what i subtracting value that is not equal to 1 – Svisstack Jan 25 '12 at 23:34
  • 5
    Then you can use [Interlocked.Add()](http://msdn.microsoft.com/en-us/library/33821kfh.aspx) instead. – Sean U Jan 25 '12 at 23:39
  • 2
    This code does guarantee that calling `IncrementDoneCounter()` twice will always increment the counter exactly twice, which is probably what the OP wanted. However, for those who are coming into this code wanting to keep *distinct* count values (e.g., for unique IDs), users of this code will need to modify it to make use of the return value of the call to `Interlocked.Increment`. – Brian Jul 28 '17 at 22:45
4

Using the Interlocked class provides for atomic operations, i.e. inherently threadsafe as in this LinqPad example:

void Main()
{
    var counters = new Counters();
    counters.DoneCounter += 34;
    var val = counters.DoneCounter;
    val.Dump(); // 34
}

public class Counters
{
    int doneCounter = 0;
    public int DoneCounter
    {
        get { return Interlocked.CompareExchange(ref doneCounter, 0, 0); }
        set { Interlocked.Exchange(ref doneCounter, value); }
    }
}
David Clarke
  • 12,888
  • 9
  • 86
  • 116
  • I recommend against this approach. `+=` is not atomic. I would recommend using `Interlocked.Add` rather than `+=` in this case. Incidentally, getting/setting an Int32 *is* atomic (see https://stackoverflow.com/a/11745604/18192), so I'm not sure what this code is trying to accomplish. – Brian Jul 28 '17 at 22:38
  • 1
    See also demo of this race condition at https://dotnetfiddle.net/TmHvku. Note that dotnetfiddle tends to avoid race conditions (probably it doesn't switch threads very frequently), so you might not be able to produce incorrect results unless you test locally. – Brian Jul 28 '17 at 22:56
2

What exactly are you trying to do with the counters? Locks don't really do much with integer properties, since reads and writes of integers are atomic with or without locking. The only benefit one can get from locks is the addition of memory barriers; one can achieve the same effect by using Threading.Thread.MemoryBarrier() before and after you read or write a shared variable.

I suspect your real problem is that you are trying to do something like "DoneCounter+=1", which--even with locking--would perform the following sequence of events:

  Acquire lock
  Get _DoneCounter
  Release lock
  Add one to value that was read
  Acquire lock
  Set _DoneCounter to computed value
  Release lock

Not very helpful, since the value might change between the get and set. What would be needed would be a method that would perform the get, computation, and set without any intervening operations. There are three ways this can be accomplished:

  1. Acquire and keep a lock during the whole operation
  2. Use Threading.Interlocked.Increment to add a value to _Counter
  3. Use a Threading.Interlocked.CompareExchange loop to update _Counter

Using any of these approaches, it's possible to compute a new value of _Counter based on the old value, in such a fashion that the value written is guaranteed to be based upon the value _Counter had at the time of the write.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
supercat
  • 77,689
  • 9
  • 166
  • 211
2

If you're expecting not just that some threads will occasionally write to the counter at the same time, but that lots of threads will keep doing so, then you want to have several counters, at least one cache-line apart from each other, and have different threads write to different counters, summing them when you need the tally.

This keeps most threads out of each others ways, which stops them from flushing each others values out of the cores, and slowing each other up. (You still need interlocked unless you can guarantee each thread will stay separate).

For the vast majority of cases, you just need to make sure the occasional bit of contention doesn't mess up the values, in which case Sean U's answer is better in every way (striped counters like this are slower for uncontested use).

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
0

You could declare the _DoneCounter variable as "volatile", to make it thread-safe. See this:

http://msdn.microsoft.com/en-us/library/x13ttww7%28v=vs.71%29.aspx

  • 1
    Hahahah the example on that page is so horribly useless, go Microsoft. This is probably the simplest way to accomplish what he is looking for. – SoWeLie Jan 25 '12 at 23:30
  • +1, semms to be working, i rewrited properties to public variable, but i cant accept you because question is about properties, i think this will not be work if volaite varaible will be inside of class (private) and will be wapped with public property – Svisstack Jan 25 '12 at 23:45
  • this should work: class c { private volatile int _n; public nValue {get { return this._n; } set { this._n = value; }} } –  Jan 25 '12 at 23:48
  • 3
    This will not work. `volatile` does not prevent threads from interleaving read and write operations. It only prevents them from using the processor cache or registers. It is for situations where threads are allowed to read from or write to a value, but never both. Anywhere else, and the only thing it does reliably is make your program run slower. – Sean U Jan 25 '12 at 23:52
  • And interlocked and locks bring their own memory barriers, meaning a lot (but not all) of the cases where those are used in writing to them, you don't need it. – Jon Hanna Jan 26 '12 at 00:00
  • this not works, i just dont get concurrency example in my code;-) – Svisstack Jan 26 '12 at 01:43