6

注意我倾向于编写无锁代码,所以我尽量避免任何类型的锁。相反,我只使用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关键字可以帮助解决这个问题,但似乎不能,因为“Volatile 不保证值的新鲜度。它阻止了一些优化,但不保证线程同步。” 正如这里所说,c# 原始数组是易失性的吗?

问题:

  • 如果没有同步,这两个问题都可能出现,我是否正确?
  • 如果你能给我一个简短的例子来证明没有同步它就行不通——那就太好了
  • 我应该如何访问双字段或变量或属性以始终具有真实的最新值?“同步”能保证“新鲜”吗?最快的方法是什么?自旋锁什么的?

double目前我在我的程序中使用了很多decimal变量/字段/属性,几乎所有东西都可以正常工作,所以我真的很困惑,因为我经常在没有任何同步的情况下从不同的线程访问它们,而这只是工作......但现在我在想使用float“内置同步”可能会更好

4

4 回答 4

3

是的,你需要做点什么。double并且不能保证是 atomicdecimal,所以如果你不保护它,你可能会得到一个撕裂的价值 - 即你的第一个子弹是完全正确的。

回复volatile; 没有实际意义;你不能有一个volatile字段是doubleor decimal,所以最简单的答案是:lock

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();
        }
    }
}

关键点;该语言不保证. double实际上,我希望你会没事的,但是线程引起的大多数微妙问题都是由于使用“我期望”而不是“我可以保证”。

于 2012-07-06T10:55:12.817 回答
0

如果你想保证一个代码块在另一个线程操作它之前被执行和完成,用一个lock.

您可能很幸运,线程可能永远不会为使用变量而争吵,但要确保它永远不会发生,确保采取预防措施不会造成伤害。

看看这里 - 这可能会有所帮助:http: //msdn.microsoft.com/en-us/library/ms173179%28v=vs.80%29.aspx

于 2012-07-06T10:44:10.367 回答
0

一般的答案是 - 应该为所有“共享”变量同步更新。确切答案需要查看代码片段。

于 2012-07-06T10:53:08.550 回答
0

是的,如果多个线程同时读/写双精度,您需要锁定以确保获得正确的结果。

这是一个失败的例子

[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);
    }
}
于 2012-07-06T11:04:56.557 回答