ThreadLocal Aggregations и параллельная библиотека задач - PullRequest
1 голос
/ 03 ноября 2010

Почему я получаю другой результат со следующими разделами кода

Пример кода 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 Ответ

1 голос
/ 25 ноября 2010

Почему вы даже пытаетесь использовать ThreadLocal хранилище здесь вместо локальной переменной внутри Задачи?Библиотека Task Parallel может повторно использовать поток для выполнения более чем одной задачи, и локальное хранилище вашего потока будет перезаписано.В первом примере это может работать, так как вы не сбрасываете его каждый раз, когда поток используется повторно, но это будет лучше:

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

...