在c#中,是否有一种更新变量(不使用锁)的方法,其中多个线程可以读取值,但更新变量的线程不重要。
我的意思是,如果一个线程更新了该值,但其他线程几分钟后没有获得更新的值,那就没问题。
有这样的方法吗?
非常感谢
答案 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.AutoResetEvent
或System.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