跨线程读取变量的值并不重要

时间:2009-08-30 04:37:19

标签: c# multithreading

在c#中,是否有一种更新变量(不使用锁)的方法,其中多个线程可以读取值,但更新变量的线程不重要。

我的意思是,如果一个线程更新了该值,但其他线程几分钟后没有获得更新的值,那就没问题。

有这样的方法吗?

非常感谢

9 个答案:

答案 0 :(得分:4)

如果更新的值对其他线程不重要,为什么变量在线程之间共享?每个线程都可以使用自己的副本。

我不清楚。您是否想要故意延迟读取值,或者您是否意味着其他线程无法立即看到更新后的值无关紧要?

在任何情况下,如果你想在没有锁定的情况下自动更新变量,你应该考虑使用Interlocked方法,但我不确定这是否是你需要的。

答案 1 :(得分:4)

这取决于你想要做什么。您可以标记字段volatile,但这对您没有任何好处。例如,我不确定volatile是否会确保递增是原子操作。换句话说,您的一个增量(或其他更新)可能会“丢失”(由另一个线程覆盖)。为此,您可以使用Interlocked.Increment

同样,这实际上取决于你想要做什么。很多时候使用锁是最容易的。

答案 2 :(得分:4)

最佳选择是使用ReaderWriterLockSlim(或ReaderWriterLock,如果您不使用.net 3.5)。提示:This tutorial应该让您入门。

答案 3 :(得分:1)

这听起来像是重新发明缓存机制的迂回方式。你有没有研究过.NET的许多缓存系统?

答案 4 :(得分:1)

您可以通过使用Interlocked.CompareExchange以原子方式更新变量,直到您成功为止。这避免了锁定的开销,同时确保了一致性,下面的伪代码应该这样做,抱歉,我不太熟悉C#。

static void atomic_add(ref int ptr, int addend){
  int previous = *ptr;
  while(1){
    int observed = Interlocked.CompareExchange(ptr, previous+addend, previous);
    if(observed == previous){
      break;
    }else{
      previous = observed;
    }
  }
}

如果第一个参数指向的值等于第三个参数,CompareExchange将用第二个参数替换第一个参数的值,并在调用CompareExchange时在ptr返回内存中的值。如果compare exchange返回的值与ptr中观察到的值相同,那么该值已成功更新并且循环中断,否则,指针中的值自上次读取后更改,之前更新,我们再次尝试。 / p>

从我读过的内容中,Interlocked.CompareExchange只存在于int32,浮点数和对象中(虽然不确定它是否完全无锁?)。我想它可以在64位平台上使用更广泛的值,但我没有任何支持它。

答案 5 :(得分:1)

假设某些线程更新了您的共享变量,如果您不使用任何同步,那么您的其他线程:

  • 可能会立即或不会立即读取更新后的值。如果没有,他们会稍后阅读
  • 短时间内会有一个不一致的值(有些会获得新值,其他人会有更新的值)

如果这些问题不重要,并且您的逻辑不依赖于共享变量的值,则不需要同步。只允许所有线程访问共享变量。

你想要达到什么目的?获得更好的答案将会有所帮助。

编辑:

正如ShuggyCoUk所指出的那样,谨慎使用这种方法。它依赖于原子读取和写入,这取决于共享变量的类型。摘录自here

  

读取和写入以下数据   类型是原子的:bool,char,byte,   sbyte,short,ushort,uint,int,   float和引用类型。在   另外,读取和写入枚举   具有基础类型的类型   以前的清单也是原子的。读   和写其他类型,包括   long,ulong,double和decimal,as   以及用户定义的类型,不是   保证是原子的。

答案 6 :(得分:1)

我会将数据放入一个不可变的“类”(因此它是一个引用类型)。

如果要更新值,首先要构建一个新实例,然后更新对它的引用(这是一个原子操作)。

每个读者线程将获得一致的旧值或一致的新值(取决于它从引用中读取的时间点)。 由于数据类是不可变的,因此无法获得中间结果!

答案 7 :(得分:0)

您可能需要查看System.Threading.AutoResetEventSystem.Threading.ManualResetEvent

它允许您跨线程触发事件(值已更改)。 ManualResetEvent可能更合适,如果你想在更新上保持懒惰......

我不确定你是否正在考虑控制对所述变量的访问......在这种情况下,同步方法可以解决这个问题:

[MethodImpl(MethodImplOptions.Synchronized)]
public void Foo()
{
    // Do foo
}

您还可以在修改其值时获取所述对象的锁:

lock(myObject)
{
    myObject = new value;
}

答案 8 :(得分:0)

使用本机原子的.Net类型
这取决于您使用的变量类型。一些变量原子地起作用。原子写意味着cpu一次性写入整个变量,或者你至少可以认为它是这样做的。这意味着没有其他线程看到部分更新的变量 - 这显然是坏的!原子行为的变量将按照您的要求行事,无需锁定。

对引用类型的赋值,bool,char,byte,sbyte,short,ushort,uint,int和float都是原子的。

我在这里得到了原子类型列表: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/a5a3c1b4-9f76-43d7-90a6-6572c59491fe

请参阅此处的C#规范的第5.5节: http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20Language%20Specification.doc