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)。您积累了一个很好的任务局部变量,但是您随后用结果覆盖了线程局部值,即使该线程局部值可能在顺序执行的多个任务之间共享(例如,每个内核一个线程)-错误#2。这将导致某些任务共享相同的线程本地值。错误 #3:当您返回线程本地值时,您会很幸运,因为它将与 temp 相同,并且没有其他线程可以更改它,因此它等同于仅在任务中使用局部变量。

在第二个示例中,您在初始化时犯了同样的错误。但是随后您继续对值进行双重计数,因为线程本地值不会在每个任务开始时重置,因此如果两个任务在同一个线程上运行,第一个任务可能返回 1+2+3,第二个可能返回 6+1+ 2+3。

于 2010-11-25T08:03:49.280 回答