6

私はロックフリーのコードを書く傾向があるので、可能な限りあらゆる種類のロックを避けるようにしています。while(true)私は多くの CPU パワーを持っているので、代わりに loop を使用します。

そのhttp://msdn.microsoft.com/en-us/library/aa691278%28VS.71%29.aspx によると、double変数の更新はアトミックではありません。

私は2つの問題について心配しています:

  • あるスレッドがフィールドまたはプロパティの変数を変更し、別のスレッドがそれを同時に読み取る場合、以前の値または新しい値を取得したいのですが、奇妙なものを受け取りたくありません。つまり、あるスレッドが値を 5.5 から 15.15 に変更した場合、別のスレッドでこれら 2 つの数値のいずれかを使用したいのですが、5.15 や 15.5 などではありません。
  • あるスレッドがすでに値を更新し、その後別のスレッドがそれ読み取った場合、最新の最新の値を受け取りたいです。volatileキーワードはそれを助けることができると考えていましたが、「Volatile は値の鮮度を保証しません。いくつかの最適化を防ぎますが、スレッドの同期を保証しません」ため、そうではないようです。ここで述べたように、C#プリミティブ配列は揮発性ですか?

質問:

  • 同期しないと、これらの問題の両方が発生する可能性があるというのは正しいですか?
  • 同期なしでは機能しないことを証明する短い例を教えていただければ幸いです-それはいいでしょう
  • double フィールド、変数、またはプロパティにアクセスして、常に実際の最新の値を取得するにはどうすればよいですか? 「同期」は「鮮度」を保証しますか?そのための最速の方法は何ですか?スピンロックか何か?

現在、私は自分のプログラムで多くの変数/フィールド/プロパティを使用doubledecimalており、ほとんどすべてが正常に動作するため、同期せずに異なるスレッドからそれらにアクセスすることがよくあり、それが機能するため、本当に混乱しています...しかし、今私は考えていますおそらく、 float「組み込みの同期」を使用する方が良いでしょう

4

4 に答える 4

3

はい、何かをする必要があります。doubleまた、atomic であることが保証されてdecimalいないため、保護しないと破れた値を取得する可能性があります。つまり、最初の箇条書きは完全に正しいです。

volatile; それは議論の余地があります。またはのフィールドを持つことは許可されていないため、最も簡単な答えは次のとおりです。volatiledoubledecimallock

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しません。実際には、問題ないと思いますが、スレッド化によって発生する微妙な問題のほとんどは、「保証できます」ではなく「期待します」を使用したことが原因です。

于 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

はい、複数のスレッドが同時に 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);
    }
}
于 2012-07-06T11:04:56.557 に答える
0

一般的な答えは、すべての「共有」変数の更新を同期する必要があるということです。正確な答えについては、コード スニペットを参照する必要があります。

于 2012-07-06T10:53:08.550 に答える