Programming

Parallel.ForEach vs Task.Run 및 Task.WhenAll

procodes 2020. 6. 21. 21:15
반응형

Parallel.ForEach vs Task.Run 및 Task.WhenAll


Parallel.ForEach 또는 Task.Run ()을 사용하여 일련의 작업을 비동기 적으로 시작하는 것의 차이점은 무엇입니까?

버전 1 :

List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
    DoSomething(s);
});

버전 2 :

List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
    Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);

이 경우 두 번째 방법은 작업을 차단하는 대신 비동기 적으로 작업을 기다립니다.

그러나 Task.Runloop- With에서 사용하는 데에는 단점이 있으며 Parallel.ForEach, Partitioner필요 이상으로 더 많은 작업을 수행하지 않기 위해 만들어집니다. Task.Run이 작업을 수행하는 동안 항상 항목 당 하나의 작업을 수행하지만 Parallel클래스 배치는 작동하므로 전체 작업 항목보다 적은 작업을 생성합니다. 이는 특히 루프 본체에 품목 당 적은 양의 작업이있는 경우 전체 성능을 크게 향상시킬 수 있습니다.

이 경우 다음과 같이 작성하여 두 옵션을 결합 할 수 있습니다.

await Task.Run(() => Parallel.ForEach(strings, s =>
{
    DoSomething(s);
}));

이 짧은 형식으로 작성 될 수도 있습니다.

await Task.Run(() => Parallel.ForEach(strings, DoSomething));

첫 번째 버전은 호출 스레드를 동 기적으로 차단하고 일부 작업을 실행합니다.
UI 스레드 인 경우 UI가 고정됩니다.

두 번째 버전은 스레드 풀에서 작업을 비동기 적으로 실행하고 완료 될 때까지 호출 스레드를 해제합니다.

사용 된 스케줄링 알고리즘에도 차이가 있습니다.

두 번째 예는 다음과 같이 단축 될 수 있습니다.

await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s)));

읽기가 더 쉬워 졌기 때문에이 작업을 마쳤습니다.

  List<Task> x = new List<Task>();
  foreach(var s in myCollectionOfObject)
  {
      // Note there is no await here. Just collection the Tasks
      x.Add(s.DoSomethingAsync());
  }
  await Task.WhenAll(x);

Parallel.ForEach가 부적절하게 사용되는 것을 보았습니다.이 질문의 예가 ​​도움이 될 것이라고 생각했습니다.

콘솔 앱에서 아래 코드를 실행하면 Parallel.ForEach에서 실행 된 작업이 호출 스레드를 차단하지 않는 방법을 볼 수 있습니다. 결과에 신경 쓰지 않으면 (긍정적이거나 부정적이지만) 결과가 필요하면 Task.WhenAll을 사용해야합니다.

using System;
using System.Linq;
using System.Threading.Tasks;

namespace ParrellelEachExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var indexes = new int[] { 1, 2, 3 };

            RunExample((prefix) => Parallel.ForEach(indexes, (i) => DoSomethingAsync(i, prefix)),
                "Parallel.Foreach");

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("*You'll notice the tasks haven't run yet, because the main thread was not blocked*");
            Console.WriteLine("Press any key to start the next example...");
            Console.ReadKey();

            RunExample((prefix) => Task.WhenAll(indexes.Select(i => DoSomethingAsync(i, prefix)).ToArray()).Wait(),
                "Task.WhenAll");
            Console.WriteLine("All tasks are done.  Press any key to close...");
            Console.ReadKey();
        }

        static void RunExample(Action<string> action, string prefix)
        {
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine($"{Environment.NewLine}Starting '{prefix}'...");
            action(prefix);
            Console.WriteLine($"{Environment.NewLine}Finished '{prefix}'{Environment.NewLine}");
        }


        static async Task DoSomethingAsync(int i, string prefix)
        {
            await Task.Delay(i * 1000);
            Console.WriteLine($"Finished: {prefix}[{i}]");
        }
    }
}

결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

결론:

작업에 Parallel.ForEach를 사용하면 호출 스레드가 차단되지 않습니다. 결과가 마음에 들면 작업을 기다리십시오.

~ 건배

참고 URL : https://stackoverflow.com/questions/19102966/parallel-foreach-vs-task-run-and-task-whenall

반응형