Parallel.ForEach() против цикла Foreach()

  • Михаил
  • 12 мин. на прочтение
  • 128
  • 27 Jul 2022
  • 27 Jul 2022

Цикл Foreach

Цикл Foreach в C# выполняется в одном потоке, и обработка выполняется последовательно один за другим. Цикл Foreach — это базовая функция C#, доступная в C# 1.0. Его выполнение в большинстве случаев медленнее, чем  Parallel.Foreach .

Цикл Parallel.ForEach

Цикл Parallel.ForEach в C# выполняется в нескольких потоках, и обработка выполняется параллельно. Цикл Parallel.ForEach не является базовой функцией C# и доступен в C# 4.0 и более поздних версиях. До C# 4.0 мы не можем его использовать. В большинстве случаев его выполнение выполняется быстрее, чем foreach. Чтобы использовать цикл Parallel.ForEach, нам нужно импортировать пространство имен System.Threading.Tasks в директиву using.

Но вы хорошо знаете свое приложение и можете решить, какое из них вы хотите использовать.

Я привожу 2 примера, в первом примере традиционный цикл foreach быстрее, чем цикл Parallel.foreach, а во втором примере традиционный цикл foreach очень медленный по сравнению с Parallel.foreach.

Пример 1. Цикл Parallel.ForEach медленнее традиционного цикла Foreach.
 

  1. Список< строка >фрукты =  новый  список< строка >();  
  2.   фрукты.Добавить( "Яблоко" );  
  3.   фрукты.Добавить( "Банан" );  
  4.   фрукты.Добавить( "Черника" );  
  5.   фрукты.Добавить( "Ежевика" );  
  6.   фрукты.Добавить( "Черная смородина" );  
  7.   фрукты.Добавить( "Черника" );  
  8.   фрукты.Добавить( "Вишня" );  
  9.   фрукты.Добавить( "Кокос" );  
  10.   фрукты.Добавить( "Клюква" );  
  11.   фрукты.Добавить( "Дата" );  
  12.   фрукты.Добавить( "Инжир" );  
  13.   фрукты.Добавить( "Виноград" );  
  14.   фрукты.Добавить( "Гуава" );  
  15.   фрукты.Добавить( "Джек-фрут" );  
  16.   Fruits.Add( "Киви" );  
  17.   фрукты.Добавить( "Лимон" );  
  18.   фрукты.Добавить( "Лайм" );  
  19.   фрукты.Добавить( "Личи" );  
  20.   фрукты.Добавить( "Манго" );  
  21.   фрукты.Добавить( "Дыня" );  
  22.   фрукты.Добавить( "Оливковое" );  
  23.   фрукты.Добавить( "Апельсин" );  
  24.   фрукты.Добавить( "Папайя" );  
  25.   фрукты.Добавить( "Слива" );  
  26.   фрукты.Добавить( "Ананас" );  
  27.   фрукты.Добавить( "Гранат" );  
  28.   
  29.   Console.WriteLine( "Печать списка с использованием цикла foreach\n" );  
  30.   
  31.   var stopWatch = Секундомер.StartNew();  
  32.   foreach  ( струнные  фрукты  во  фруктах)  
  33.   {  
  34.       Console.WriteLine( "Имя плода: {0}, идентификатор потока = {1}" , плод, Thread.CurrentThread.ManagedThreadId);  
  35.   }  
  36.   Console.WriteLine( "время выполнения цикла foreach = {0} секунд\n" , stopWatch.Elapsed.TotalSeconds);  
  37.   Console.WriteLine( "Печать списка с помощью Parallel.ForEach" );  
  38.   
  39.   
  40.   секундомер = Секундомер.НачатьНовый();  
  41.   Parallel.ForEach(фрукты, фрукты =>  
  42.   {  
  43.       Console.WriteLine( "Имя плода: {0}, идентификатор потока = {1}" , плод, Thread.CurrentThread.ManagedThreadId);  
  44.   
  45.   }  
  46.   );  
  47.   Console.WriteLine( "Время выполнения Parallel.ForEach() = {0} секунд" , stopWatch.Elapsed.TotalSeconds);  
  48.   Консоль.Прочитать();  

Выход

Время исполнения 

Выход 

Пример 2. Цикл Parallel.ForEach быстрее традиционного цикла Foreach.

Традиционный Foreach 

  1. var stopWatch = Секундомер.StartNew();  
  2. PointF firstLocation =  new  PointF(10f, 10f);  
  3. PointF secondLocation =  новая  точкаF(10f, 50f);  
  4. foreach ( строковый  файл  в  Directory.GetFiles(@  "D:\Images" ))  
  5. {  
  6.     Растровое изображение = (Растровое изображение) Image.FromFile(файл);  
  7.     используя (Graphics Graphics = Graphics.FromImage (растровое изображение))  
  8.     {  
  9.         используя (Шрифт arialFont =  новый  шрифт ( "Arial" , 10))  
  10.         {  
  11.             Graphics.DrawString( "Банкетешвар" , arialFont, Brushes.Blue, firstLocation);  
  12.             Graphics.DrawString( "Narayan" , arialFont, Brushes.Red, secondLocation);  
  13.         }  
  14.     }  
  15.     bitmap.Save(Path.GetDirectoryName(file) +  "Foreachloop" "\\"  + Path.GetFileNameWithoutExtension(file) + Guid.NewGuid()  
  16.         .ToString() +  ".jpg" );  
  17. }  
  18. Console.WriteLine( "время выполнения цикла foreach = {0} секунд\n" , stopWatch.Elapsed.TotalSeconds);  

Выход

команда 

Parallel.foreach

  1. var stopWatch = Секундомер.StartNew();  
  2. PointF firstLocation =  new  PointF(10f, 10f);  
  3. PointF secondLocation =  новая  точкаF(10f, 50f);  
  4. Parallel.ForEach(Directory.GetFiles(@  "D:\Images" ), файл =>  
  5. {  
  6.     Растровое изображение = (Растровое изображение) Image.FromFile(файл);  
  7.     используя (Graphics Graphics = Graphics.FromImage (растровое изображение))  
  8.     {  
  9.         используя (Шрифт arialFont =  новый  шрифт ( "Arial" , 10))  
  10.         {  
  11.             Graphics.DrawString( "Банкетешвар" , arialFont, Brushes.Blue, firstLocation);  
  12.             Graphics.DrawString( "Narayan" , arialFont, Brushes.Red, secondLocation);  
  13.         }  
  14.     }  
  15.     bitmap.Save(Path.GetDirectoryName(file) +  "Parallel" "\\"  + Path.GetFileNameWithoutExtension(file) + Guid.NewGuid()  
  16.         .ToString() +  ".jpg" );  
  17. });  
  18. Console.WriteLine( "Время выполнения Parallel.ForEach() = {0} секунд" , stopWatch.Elapsed.TotalSeconds);  
  19. Консоль.Прочитать();  

Выход

бежать 

Чтобы проверить производительность приведенного выше кода, я использовал около 150 изображений в обоих случаях.

Вы можете видеть, что если вы выполняете какую-либо массовую задачу внутри цикла foreach, то parallel.foreach выполняется очень быстро, поэтому вы можете использовать parallel.foreach. Но если вы просто повторяете и выполняете очень небольшую задачу внутри цикла, тогда используйте традиционный цикл for