我应该*始终*同步访问从多个线程使用的所有双字段/属性/变量?

jav*_*red 5 c# synchronization

注意我倾向于编写无锁代码,所以我尽可能避免任何类型的锁.相反,我只是使用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#原始数组volatile?

问题:

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

目前我在我的程序中使用了很多doubledecimal变量/字段/属性,几乎每个都工作正常,所以我真的很困惑,因为我从不同的线程访问它们没有任何同步,这只是工作...但现在我在想可能最好用float"内置同步化"

Mar*_*ell 3

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

关于volatile; 这是没有实际意义的;不允许有一个字段是或,所以最简单的答案是volatiledoubledecimallock

失败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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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