注意:我倾向于写无锁代码,所以我尽量避免锁的任何类型的在可能的情况。 相反,我只是用while(true)
循环,因为我有很多的CPU功率。
根据该http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx double
变量更新不是原子。
我很担心两个问题:
- 如果一个线程修改字段或属性的变量,而另一个线程在同一时间 ,我想要么以前的或新的值读它,但我不希望收到一些奇怪的事情。 也就是说,如果一个线程的变化值从5.5到15.15我想在另一个线程这两个数字中的一个,但不是5.15或15.5或其他任何东西。
- 如果一个线程已经更新的值,而另一个线程读它之后 ,我想收到最新的,最新的,值。 我在想,volatile关键字可以与帮助,但似乎不能,因为“挥发性不保证值的新鲜感。它可以防止一些优化,但不保证线程的同步。” 因为这里说的是C#基本数组挥发性?
问题:
- 我是正确,如果没有同步这两个问题可能会出现在哪里?
- 如果你能给我这证明,没有同步的,它不会工作简单的例子 - 那将是不错
- 我应该如何访问双倍字段或变量或属性始终有真正的最新的价值? 将“同步”保证“新鲜”? 什么是该最快的方法是什么? 自旋锁什么的?
目前我使用了很多double
和decimal
在我的程序中的变量/场/性能几乎everythig工作得很好,所以我真的很困惑,因为我ofthen从不同的线程访问他们没有任何同步,并且只是工作......但现在我心想大概这将是更好地使用float
有“内置syncrhonization”
是的,你需要做的事情。 double
和decimal
不能保证是原子的 ,所以如果你不保护它,你可以得到一个撕裂值-即你的第一个项目是完全正确的。
再volatile
; 这是没有实际意义; 你不能有volatile
场是double
或decimal
,所以最简单的答案是: lock
。
获得double
失败是皇室的PITA; 但这里有一个撕破值例子特色decimal
(注意成功的数字/失败会改变每一次迭代中,即使数据是一样的,这是线程调度的随机性):
using System;
using System.Threading;
static class Program
{
private static decimal shared ;
static void Main()
{
Random random = new Random(12345);
decimal[] values = new decimal[20];
Console.WriteLine("Values:");
for (int i = 0; i < values.Length; i++)
{
values[i] = (decimal)random.NextDouble();
Console.WriteLine(values[i]);
}
Console.WriteLine();
object allAtOnce = new object();
int waiting = 10;
shared = values[0];
int correct = 0, fail = 0;
for(int i = 0 ; i < 10 ; i++)
{
Thread thread = new Thread(() =>
{
lock(allAtOnce)
{
if (Interlocked.Decrement(ref waiting) == 0)
{
Monitor.PulseAll(allAtOnce);
} else
{
Monitor.Wait(allAtOnce);
}
}
for(int j = 0 ; j < 1000 ; j++)
{
for(int k = 0 ; k < values.Length ; k++)
{
Thread.MemoryBarrier();
var tmp = shared;
if(Array.IndexOf(values, tmp) < 0)
{
Console.WriteLine("Invalid value detected: " + tmp);
Interlocked.Increment(ref fail);
} else
{
Interlocked.Increment(ref correct);
}
shared = values[k];
}
}
if (Interlocked.Increment(ref waiting) == 10)
{
Console.WriteLine("{0} correct, {1} fails",
Interlocked.CompareExchange(ref correct, 0, 0),
Interlocked.CompareExchange(ref fail, 0, 0));
Console.WriteLine("All done; press any key");
Console.ReadKey();
}
});
thread.IsBackground = false;
thread.Start();
}
}
}
关键点; 语言不作任何担保的原子double
。 在现实中,我希望你会没事的,反而造成线程最微妙的问题是由于使用“我希望”,而不是“我可以保证。”
如果你想保证另一个线程操作它之前的代码块将被执行,完成,围绕代码块与lock
。
你可能是幸运的,和线程可能永远不会在战斗使用变量,但要确保它永远不会发生,确保预防措施不会伤害。
看看这里-这可能帮助: http://msdn.microsoft.com/en-us/library/ms173179%28v=vs.80%29.aspx
一般的答案是 - 更新应该为所有的“共享”变量同步。 对于确切的答案需要看到的代码片断。
是的,你需要锁定,以确保您得到正确的结果,如果多线程读取/在同一时间写了一倍。
下面是一个失败的例子
[TestFixture]
public class DoubleTest
{
private double theDouble;
[Test]
public void ShouldFailCalledParallell()
{
theDouble = 0;
const int noOfTasks = 100;
const int noOfLoopInTask = 100;
var tasks = new Task[noOfTasks];
for (var i = 0; i < noOfTasks; i++)
{
tasks[i] = new Task(() =>
{
for (var j = 0; j < noOfLoopInTask; j++)
{
theDouble++;
}
});
}
foreach (var task in tasks)
{
task.Start();
}
Task.WaitAll(tasks);
theDouble.Should().Be.EqualTo(noOfTasks * noOfLoopInTask);
}
}
文章来源: should I *always* synchronize access to all double field/property/variables that used from more than one thread?