1

Почему я получить другой результат со следующими секциями кодыThreadLocal Скопление и Task Parallel Library

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

Почему вы даже пытаетесь использовать здесь 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.

+0

Речь идет не столько о том, зачем вам это делать, но и об этом техническом правиле. – Blair

+0

Я добавил более подробное объяснение, почему оба примера являются «неправильными». –