ThreadLocal聚合和任务并行库

时间:2010-11-03 06:09:37

标签: unix concurrency parallel-processing task-parallel-library thread-local

为什么我在下面的代码部分得到不同的结果

代码示例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();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

为什么你甚至试图在这里使用ThreadLocal存储而不仅仅是在Task中使用局部变量?任务并行库可能会重用一个线程来执行多个任务,并且您的线程本地存储将被覆盖。在第一个例子中,它可能有效,因为每次重用线程时你都没有重置它,但这样会更好:

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

在您的第二个示例中,您在初始化时犯了同样的错误。但是你继续重复计算值,因为线程本地值没有在每个任务的开始重置,所以如果两个任务在同一个线程上运行,第一个可能返回1 + 2 + 3而第二个可能返回6 + 1 + 2 + 3。