lock()在这里有意义吗?

时间:2011-03-14 14:51:59

标签: .net multithreading locking atomic interlocked

我有一个简单的类:

  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; } }
  }

,一个用于设置值的线程和一个读取值的多个线程。因此,可以使用lock()使所有读取和写入不间断。 但我知道同步永远不会实现,Val1 - Val2总是有可能不等于Delta ,我不在乎 STRONG>。我关心的更多是通过吸气剂获得稳定值。但是lock()对于这种情况来说是昂贵的,因为大多数读者都会工作。

我想到的最好的事情是使用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; 
      } 
    }

但代码对我来说似乎很笨。我不知道。

lock()也有意义吗?我应该使用Interlocked.Exchange()来获得性能提升吗?或者我还能做什么?

3 个答案:

答案 0 :(得分:3)

您需要锁定整个SetValues方法:

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; } } }

读者仍然可以获得不属于一起的Val1,Val2和Delta,因为他们分几步读取它。

您可以将Val1,Val2和Delta放入一个值对象中,该对象可以一次检索并且不会更改:

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 /* ... */ }
}

答案 1 :(得分:2)

对于多个读者/作者场景,我会使用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.

答案 2 :(得分:2)

如果你需要getters返回稳定的结果,并且只需要保持内部同步,那么我建议你创建一个名为“Snapshot”的类(包含Val1,Val2和Delta)或类似的东西。在您的setter中,构建此类的新副本并将其交换为实例变量。在getter中,只需返回快照的当前副本。只要呼叫者需要一致的体验,他们就会使用从单个getter呼叫返回的单个快照实例。

所以你不得不放弃多个吸气剂 - 没有办法(没有外部同步)来保证Val1,Val2和Delta一致。


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; } }

  }