6

ここには基本的なものが欠けている可能性がありますが、これを理解するために親切な助けをいただければ幸いです。だから私は私が書いた次の単純なマルチスレッドプログラムを持っています:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            //            List<int> outcome = new List<int>();
            Test t = new Test();

                Thread thread1 = new Thread(new ThreadStart(t.call1));
                Thread thread2 = new Thread(new ThreadStart(t.call2));
                thread1.Start();
                thread2.Start();
                Thread.Sleep(3000); //Give enough time for threads to end
                Console.Write("{0},", t.mSum);
                t.mSum = 0;
        }
    }

    class Test
    {
        public int mSum = 0;
        public void call1()
        {
            //lock (this)
            //{

            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("Hello Thread 1, mSum value: {0}", mSum);
                mSum = mSum + 1;
                Console.WriteLine("Goodbye Thread 1, mSum value: {0}", mSum);
            }
            //}
            //  Console.WriteLine(mSum);
        }
        public void call2()
        {
            for (int i = 0; i < 100 ; i++)
            {
                Console.WriteLine("Hello Thread 2, mSum value: {0}",mSum);
                mSum = mSum + 1;
                Console.WriteLine("Goodbye Thread 2, mSum value: {0}",mSum);
            }
        }
    }
}    

コンテキストの切り替えはいつでも発生する可能性があるため、この出力は非決定論的であると予想されますか? しかし、プログラムを実行すると、次の出力が得られます (stackoverflow.com の質問投稿スキルが不十分なため、出力の一部のみが不正です)。

こんにちはスレッド 1、mSum 値: 62 さようならスレッド 1、mSum 値: 63   
こんにちはスレッド 1、mSum 値: 63 さようならスレッド 1、mSum 値: 64   
こんにちはスレッド 2、mSum 値: 59 さようならスレッド 2、mSum 値: 65   
こんにちはスレッド 2、mSum 値: 65 さようならスレッド 2、mSum 値: 66

したがって、私がこれを正しく書いたと仮定すると、mSum は実際にスレッド間で共有されます (そのように見えます...) - 行番号をどのように説明できますか? 3? スレッド 2 は 59 を読み取り、1 を加算すると 65 になります!

新しい種類の数学を発見しましたか? :)

4

4 に答える 4

10

共有変数mSumをロックしてmSum = mSum + 1おらず、アトミック操作ではありません。そして、コンソールへの出力、変数のインクリメント、そして再びコンソールへの出力がアトミックではないことは明らかです:)スレッドがインターレースする可能性のある方法はたくさんあります。例えば:

1) mSum = 0 [スレッド 1 が動作中]

2) mSum = 1 [スレッド 1 が動作中]

3) mSum = 2 [スレッド 2 が動作中]

4) ...

5) mSum = 59 [Thread2 is working] で、"Hello..." の後に横取りされます。

6) mSum = 60 [スレッド 1 が動作中]

7) mSum = 61 [スレッド 1 が動作中]

8) ...

9) mSum = 64 [Thread2 が動作中] 増分ラインの直前で起床 Thread2 が継続し、65 を計算

でメモリからmSumを読み取った後でも、を計算する前5) Thread2にプリエンプトされる可能性があります。mSum = mSum + 1mSum + 1

于 2013-08-23T12:48:38.693 に答える
4

複数のスレッドを使用しているため、最初の呼び出しと 2 番目の呼び出しの間で値が変更されている可能性がありConsole.WriteLineます。

追加が使用する正しい値を報告していることを確認したい場合は、ロックを使用する必要があります。

于 2013-08-23T12:49:44.433 に答える
1

最初の WriteLine と 2 番目の WriteLine の間で同時に 2 つのスレッド A と B から mSum が変更されます mSum が変更される可能性があります。mSum はロックされていないか揮発性ではないため、メモリにバリアがないため、非常に奇妙な結果になる可能性がありますが、CPU の種類とキャッシュによって異なります。

mSum の前に volatile という単語を入れるだけで、mSum が CPU にキャッシュされないことを意味します。アプリケーションを再度実行すると、コンソールが表示されます!?

于 2013-08-23T13:00:00.353 に答える