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.Run
루프에서 사용 하는 데는 단점이 있습니다 . 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를 Task와 함께 사용하면 호출 스레드가 차단되지 않습니다. 결과에 관심이 있다면 작업을 기다려야합니다.
~ 건배