1

以下のコードのセクションで異なる結果が得られるのはなぜですか

コード例 1

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

namespace ThreadLocalTasks
{
    class Program
    {
        static void Main(string[] args)
        {

            ThreadLocal<int> aggregrations = new ThreadLocal<int>();
            Task<int>[] tasks = new Task<int>[10];

            for (int i = 0; i < tasks.Length; i++)
            {
                aggregrations.Value = 0;
                int tempi = i;
                tasks[tempi] = new Task<int>(() =>
                {
                    int temp = 0;
                    for (int j = 1; j <= 3; j++)
                    {
                        temp += j;
                    }
                    aggregrations.Value = temp;
                    return aggregrations.Value;
                });

            }

            tasks.ToList().ForEach(x => {
                x.Start();
            });

            Task.WaitAll(tasks);

            int sum = 0;

            tasks.ToList().ForEach(x =>
            {
                sum += x.Result;
            });

            Console.WriteLine("Sum: {0}", sum);

            Console.WriteLine("Press any key to quit..");
            Console.ReadKey();
        }
    }
}

サンプル 2

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

namespace ThreadLocalTasks
{
    class Program
    {
        static void Main(string[] args)
        {

            ThreadLocal<int> aggregrations = new ThreadLocal<int>();
            Task<int>[] tasks = new Task<int>[10];

            for (int i = 0; i < tasks.Length; i++)
            {
                aggregrations.Value = 0;
                int tempi = i;
                tasks[tempi] = new Task<int>(() =>
                {
                    for (int j = 1; j <= 3; j++)
                    {
                        aggregrations.Value += j;
                    }
                    return aggregrations.Value;
                });

            }

            tasks.ToList().ForEach(x => {
                x.Start();
            });

            Task.WaitAll(tasks);

            int sum = 0;

            tasks.ToList().ForEach(x =>
            {
                sum += x.Result;
            });

            Console.WriteLine("Sum: {0}", sum);

            Console.WriteLine("Press any key to quit..");
            Console.ReadKey();
        }
    }
}
4

1 に答える 1

1

ThreadLocalタスク内のローカル変数だけでなく、ここでストレージを使用しようとしているのはなぜですか? タスク並列ライブラリはスレッドを再利用して複数のタスクを実行する可能性があり、スレッド ローカル ストレージが上書きされます。最初の例では、スレッドが再利用されるたびにリセットしないのでうまくいくかもしれませんが、これはより良いでしょう:

for (int i = 0; i < tasks.Length; i++)
        {
            tasks[i] = new Task<int>(() =>
            {
                int sum = 0;
                for (int j = 1; j <= 3; j++)
                {
                    sum += j;
                }
                return sum;
            });

        }

あなたのコードが実際に何をするかについての説明:

最初の例では、起動スレッドで単一のスレッド ローカル値を 0 に初期化しますが、複数回実行します (初期化を for ループに入れて意図したものではありません - バグ #1)。タスクローカル変数に蓄積するのは良いことですが、そのスレッドローカル値が順次実行される複数のタスク間で共有される可能性がある場合でも (コアごとに 1 つのスレッドなど)、スレッドローカル値を結果で上書きします - バグ #2。これにより、一部のタスクが同じスレッド ローカル値を共有するようになります。バグ #3: スレッド ローカル値を返すと、それは temp と同じになり、他のスレッドがそれを変更できず、タスク内でローカル変数を使用するのと同じになるため、幸運になります。

2番目の例では、初期化で同じ間違いを犯しています。ただし、各タスクの開始時にスレッド ローカル値がリセットされないため、カウント値が 2 倍になります。同じスレッドで 2 つのタスクが実行された場合、最初のタスクが 1+2+3 を返し、2 番目のタスクが 6+1+ を返す可能性があります。 2+3。

于 2010-11-25T08:03:49.280 に答える