我正在使用32位计算机运行,并且我能够使用以下快速命中的代码片段来确认长值可以撕裂.
static void TestTearingLong()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start();
System.Threading.Thread B = new System.Threading.Thread(ThreadB);
B.Start();
}
static ulong s_x;
static void ThreadA()
{
int i = 0;
while (true)
{
s_x = (i & 1) == 0 ? 0x0L : 0xaaaabbbbccccddddL;
i++;
}
}
static void ThreadB()
{
while (true)
{
ulong x = s_x;
Debug.Assert(x == 0x0L || x == 0xaaaabbbbccccddddL);
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我尝试与双打类似的东西时,我无法得到任何撕裂.有谁知道为什么?据我从规范中可以看出,只有浮点数的赋值才是原子的.分配给双人应该有撕裂的风险.
static double s_x;
static void TestTearingDouble()
{
System.Threading.Thread A = new System.Threading.Thread(ThreadA);
A.Start(); …Run Code Online (Sandbox Code Playgroud) 更新:我在Eric Lippert对另一个问题的回答中偶然发现了这个问题(他引用了这个规范):
其他类型的读写,包括long,ulong,double和decimal,以及用户定义的类型,不保证是原子的.
好了,阅读double是不是原子.这意味着可以在读取中间修改该值,对吧?那么如何以double原子方式读取值?
我注意到有一种价值Interlocked.Read方法long.这对我来说很有意义,因为读取64位值必须需要两个步骤,因此就像其他所有非原子动作一样受到竞争条件的影响.
但目前还没有Interlocked.Read对double价值观,即使System.Double是一个64位的值.
我在我的程序中看到一些奇怪的行为,我的GUI double在文本框中显示,而double其他线程也经常更新,在大多数时间显示正确的值(在200.0附近),然后偶尔随机显示错误值(如-0.08).
也许这是一个线程问题,或者也许是其他问题.但首先我想缩小可能性.那么:正在阅读double线程安全吗?
谁能解释一下,为什么这个程序返回sqrt_min的正确值?
int n = 1000000;
double[] myArr = new double[n];
for(int i = n-1 ; i>= 0; i--){ myArr[i] = (double)i;}
// sqrt_min contains minimal sqrt-value
double sqrt_min = double.MaxValue;
Parallel.ForEach(myArr, num =>
{
double sqrt = Math.Sqrt(num); // some time consuming calculation that should be parallized
if(sqrt < sqrt_min){ sqrt_min = sqrt;}
});
Console.WriteLine("minimum: "+sqrt_min);
Run Code Online (Sandbox Code Playgroud) 给定一个struct数组:
public struct Instrument
{
public double NoS;
public double Last;
}
var a1 = new Instrument[100];
Run Code Online (Sandbox Code Playgroud)
并且一个线程任务池正在写入这些元素,因为单个元素最多可以同时由两个线程写入,每个双字段一个(有效地通过主题进行上游排队).
并且知道双重可以在64位上原子地写入.(编辑这个错误地说原来是32位)
我需要使用数组中的所有值定期执行计算,并且我希望它们在计算期间保持一致.
所以我可以使用以下方法对数组进
var snapshot = a1.Clone();
Run Code Online (Sandbox Code Playgroud)
现在我的问题是关于同步的具体细节.如果我使成员易变,我认为这根本不会帮助克隆,因为读/写aquire/releases不在数组级别.
现在我可以有一个数组锁,但这会在最常见的数据写入数组的过程中引起很多争论.所以不太理想.
或者我可以有一个每行锁定,但这将是一个真正的痛苦,因为他们都需要在克隆之前被获取,同时我有所有备份的写入.
现在,如果快照没有最新值(如果它只是微秒等)并不重要,所以我想我可能会因为没有锁而逃脱.我唯一担心的是,是否存在持续时间段内没有缓存写回的情况.这是我应该担心的吗?编写器在TPL数据流中,唯一的逻辑是在结构中设置两个字段.我真的不知道函数范围是如何或者是否与缓存回写相关联.
思考/建议吗?
编辑:如果我使用互锁写入结构中的变量怎么样?
edit2:写入量比读取量高很多.写入Nos&Last字段还有两个单独和并发的服务.所以他们可以同时写入.这导致参考对象方法存在原子性问题.
edit3:更多细节.假设数组是30-1000个元素,每个元素可以每秒多次更新.