1

I have a simple class like :

  public class XXX
  {
    double val1;
    double val2;
    double delta;
    public void SetValues(double v1, double v2)
    {
      val1 = v1;
      val2 = v2;
      delta = val1 - val2;
    }

    public double Val1 { get { return val1; } }
    public double Val2 { get { return val2; } }
    public double Delta { get { return delta; } }
  }

, one thread for setting values and multiple threads that read the values. So one can use lock() to make all read and writes uninterrupted. But I am aware that synchronization is never achived, there is always a chance that Val1 - Val2 may not be equal to Delta which I do not care so much. My concern is more about obtaining the stable values via getters. However lock() is expensive for this situation because mostly the readers will work.

The next best thing that come to my mind is using Interlocked.Exchange()

    public void SetValues(double v1, double v2)
    {
      Interlocked.Exchange(ref val1, v1);
      Interlocked.Exchange(ref val2, v2);
      Interlocked.Exchange(ref delta, v1 - v2);
    }

    public double Val1 { 
      get 
      {
        double val = 0;
        Interlocked.Exchange(ref val, val1);
        return val; 
      } 
    }

But the code seems pretty dum to me. I don't know.

So does lock() makes sense? Should I use the Interlocked.Exchange() for performance gain? Or what else can I do?

ali_bahoo
  • 4,732
  • 6
  • 41
  • 63

3 Answers3

3

You need to lock the whole SetValues method:

private object lockObject = new object();

public void SetValues(double v1, double v2)
{
  lock(lockObject)
  {
    val1 = v1;
    val2 = v2;
    delta = val1 - val2;
  }
}

public double Val1 { get { lock(lockObject) { return val1; } } }
public double Val2 { get { lock(lockObject) { return val2; } } }
public double Delta { get { lock(lockObject) { return delta; } } }

The readers still can get a Val1, Val2 and Delta which do not belong together, because they read it in several steps.

You may put Val1, Val2 and Delta into a value object which van be retrieved in once and which does not change:

public Values Val1
{ 
  get 
  { 
    lock(lockObject) 
    { 
      // create a consistent object which holds a copy of the values
      return new Values(val1, val2, delta); 
    } 
  }
}

struct Values
{
  // ...
  public double Val1 { get /* ... */ }
  public double Val2 { get /* ... */ }
  public double Delta  { get /* ... */ }
}
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
2

For multiple readers / writers scenarios I would use ReaderWriterLockSlim.

Represents a lock that is used to manage access to a resource, 
allowing multiple threads for reading or exclusive access for writing.

Use ReaderWriterLockSlim to protect a resource that is read by multiple 
threads and written to by one thread at a time. ReaderWriterLockSlim 
allows multiple threads to be in read mode, allows one thread to be in 
write mode with exclusive ownership of the lock, and allows one thread 
that has read access to be in upgradeable read mode, from which the 
thread can upgrade to write mode without having to relinquish its 
read access to the resource.
Jakub Konecki
  • 45,581
  • 7
  • 87
  • 126
  • A standard `lock` can often work out cheaper than a `ReaderWriterLockSlim`, depending on the ratio of reads to writes. – LukeH Mar 14 '11 at 15:01
  • @LukeH - http://stackoverflow.com/questions/4217398/when-is-readerwriterlockslim-better-than-a-simple-lock/4217515#4217515 – Jakub Konecki Mar 14 '11 at 15:02
2

If you need the getters to return a stable result, and need to maintain only internal synchronization, then I'd recommend you create a class called "Snapshot" (containing Val1,Val2 and Delta) or something similar. In your setter, build a new copy of this class and Exchange it into an instance variable. In your getter, just return the current copy of the snapshot. So long as the callers need a consistent experience, they'd use that single snapshot instance returned from a single getter call.

So you have to abandon having multiple getters - there's no way (without external synchronization) to guarantee that Val1, Val2 and Delta would be consistent otherwise.


public class XXX
  {
    public class Snapshot {
      double val1;
      double val2;
      double delta;
      public Snapshot (double val1,double val2)
      {
         this.val1 = val1;
         this.val2 = val2;
         this.delta = val1 - val2;
      }
      public double Val1 { get { return val1; } }
      public double Val2 { get { return val2; } }
      public double Delta { get { return delta; } }
    }
    Snapshot _current;
    public void SetValues(double v1, double v2)
    {
      Snapshot s = new Snapshot(v1,v2);
      /* If there were subsequent steps needed to get the snapshot "ready", you could do them here.
         Otherwise, I think you can do this as a single assignment into _current above */
      _current = s;
    }

    public Snapshot Current { get { return _current; } }

  }
Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • +1, but the `Interlocked.Exchange` call is unnecessary since reference assignment is guaranteed to be atomic anyway. Doing it this way can be completely lock-free. – LukeH Mar 14 '11 at 15:11
  • @LukeH - I honestly couldn't remember. And if that's true, what is the purpose of `Interlocked.Exchange(Object,Object)`? – Damien_The_Unbeliever Mar 14 '11 at 15:13
  • Well, I suppose you might want to get the old value as part of the atomic operation: For example, `var oldVal = _val; _val = newVal;`, both operations are individually atomic but there's no guarantee that `oldVal` will contain the contents of `_val` at the point immediately prior to being overwritten by `newVal`. – LukeH Mar 14 '11 at 15:17
  • @LukeH - ah yes, I forgot that it's the return value that's useful there. – Damien_The_Unbeliever Mar 14 '11 at 15:20
  • Still not consistent. What if `Snapshot` is invalidated right after `xxx.Current.Val1`, before `xxx.Current.Val2`, during the execution of `xxx.Current.Val1 - xxx.Current.Val2`. This line is composed of multiple asm codes. OS might wait the thread after it gets `Val1`. – ali_bahoo Mar 14 '11 at 15:30
  • 1
    @sad_man - you have to store `xxx.Current` into a variable in your reading code, and then use that for consistency. Hence my reference to "they'd use that single snapshot instance returned from a single getter call" – Damien_The_Unbeliever Mar 14 '11 at 15:32
  • +1 but I would turn `Current` into a `GetCurrent()` method instead of a property, because it becomes clear that you can't expect `xxx.GetCurrent().Val1 - xxx.GetCurrent().Val1 == xxx.GetCurrent().Delta`, while `xxx.Current.Val1 - xxx.Current.Val1 == xxx.Current.Delta` is somewhat less obvious. – R. Martinho Fernandes Mar 14 '11 at 18:46