답변:
첫 번째는 훨씬 더 나은 옵션입니다.
Parallel.ForEach는 내부적으로 a Partitioner<T>
를 사용 하여 컬렉션을 작업 항목으로 배포합니다. 항목 당 하나의 작업을 수행하지 않고이를 일괄 처리하여 관련 오버 헤드를 줄입니다.
두 번째 옵션은 Task
컬렉션의 항목 당 하나의 일정을 예약합니다 . 결과는 (거의) 거의 같지만, 이는 특히 대규모 컬렉션의 경우 필요한 것보다 훨씬 많은 오버 헤드를 발생시키고 전체 런타임이 느려집니다.
참고- 필요한 경우 Parallel.ForEach에 대한 적절한 과부하를 사용하여 사용되는 파티 셔 너를 제어 할 수 있습니다 . 자세한 내용 은 MSDN의 Custom Partitioners 를 참조하십시오 .
런타임시 주요 차이점은 두 번째는 비동기 적으로 작동한다는 것입니다. Parallel.ForEach를 사용하여 다음을 수행하여 복제 할 수 있습니다.
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
이렇게해도 여전히 파티 셔 너를 이용하지만 작업이 완료 될 때까지 차단하지 마십시오.
"Parallel.For"로 "1,000,000,000 (십억)"번을 실행하고 "Task"객체로 하나를 실행하는 작은 실험을했습니다.
프로세서 시간을 측정하고 Parallel이 더 효율적이라는 것을 알았습니다. 병렬 : 작업을 작은 작업 항목으로 나누고 모든 코어에서 병렬로 최적의 방식으로 실행합니다. 많은 작업 개체를 만드는 동안 (FYI TPL은 내부적으로 스레드 풀링을 사용함) 각 작업에서 모든 실행을 이동하여 아래 실험에서 분명한 상자에 더 많은 스트레스를 만듭니다.
또한 기본 TPL을 설명하는 작은 비디오를 만들었으며 Parallel.For 가 일반적인 작업 및 스레드와 비교하여 http://www.youtube.com/watch?v=No7QqSc5cl8 을 보다 효율적으로 활용하는 방법을 설명했습니다 .
실험 1
Parallel.For(0, 1000000000, x => Method1());
실험 2
for (int i = 0; i < 1000000000; i++)
{
Task o = new Task(Method1);
o.Start();
}
Mehthod1()
이 예에서 무엇을 합니까?
내 생각에 가장 현실적인 시나리오는 작업을 완료하기 위해 많은 작업이 필요한 경우입니다. Shivprasad의 접근 방식은 컴퓨팅 자체보다는 객체 생성 / 메모리 할당에 더 중점을 둡니다. 나는 다음과 같은 방법으로 전화를 걸었다.
public static double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
이 방법을 실행하는 데 약 0.5 초가 걸립니다.
Parallel을 사용하여 200 번 호출했습니다.
Parallel.For(0, 200, (int i) =>
{
SumRootN(10);
});
그런 다음 구식 방식으로 200 번 호출했습니다.
List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
Task t = new Task(() => SumRootN(10));
t.Start();
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
첫 번째 사건은 26656ms에 완료되었고 두 번째 사건은 24478ms에 완료되었습니다. 나는 그것을 여러 번 반복했다. 두 번째 접근법은 한계가 빠릅니다.