注意我倾向于编写无锁代码,因此我尽量避免使用任何类型的锁。相反,我只使用while(true)
循环,因为我有很多CPU能力。
根据http://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx double
变量更新不是原子的。
我担心两个问题:
问题:
目前我在程序中使用了很多double
和decimal
变量/字段/属性,而且几乎每个都工作正常,所以我真的很困惑,因为我从不同的线程访问它们而没有任何同步和只是有效...但现在我想,使用float
进行“内置同步化”可能会更好
答案 0 :(得分:3)
是的,你需要做点什么。 double
和decimal
不保证是原子,所以如果你不保护它,你可能会得到一个撕裂的价值 - 即你的第一颗子弹是完全正确的。
Re volatile
;这是没有意义的; 不允许拥有volatile
或double
的{{1}}字段,因此最简单的答案是:decimal
。
让lock
失败是皇家PITA;但是这里有一个以double
为特征的撕裂值示例(请注意,即使数据相同,成功/失败的数量也会改变每次迭代;这是线程调度的随机性):
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();
}
}
}
的原子性无保证。实际上,我希望你会好起来的,但线程引起的大多数细微问题都是由于使用“我期待”而不是“我可以保证”。
答案 1 :(得分:0)
如果你想保证在另一个线程操作它之前执行并完成一段代码,请用lock
包围该代码块。
你可能很幸运,线程可能永远不会使用变量,但要确保它永远不会发生,确保采取预防措施不会受到伤害。
看看这里 - 这可能会有所帮助:http://msdn.microsoft.com/en-us/library/ms173179%28v=vs.80%29.aspx
答案 2 :(得分:0)
一般答案是 - 应为所有“共享”变量同步更新。要获得准确答案,请查看代码段。
答案 3 :(得分:0)
是的,如果多个线程同时读/写一个double,你需要锁定以确保得到正确的结果。
这是一个失败的例子
[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);
}
}