code

Parallel.ForEach는 활성 스레드 수를 제한합니까?

codestyles 2020. 8. 19. 08:09
반응형

Parallel.ForEach는 활성 스레드 수를 제한합니까?


이 코드가 주어지면 :

var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
    DoSomething(someString);
});

1000 개의 모든 스레드가 거의 동시에 생성됩니까?


아니요, 1000 개의 스레드를 시작하지 않습니다. 예, 사용되는 스레드 수를 제한합니다. Parallel Extensions는 물리적으로 보유한 코어 수 이미 사용중인 코어 수에 따라 적절한 수의 코어를 사용합니다 . 각 코어에 작업을 할당 한 다음 작업 도용 이라는 기술을 사용하여 각 스레드가 자체 큐를 효율적으로 처리하고 실제로 필요할 때만 값 비싼 크로스 스레드 액세스를 수행하면됩니다.

상기 봐 가지고 PFX 팀 블로그 에 대한 부하 가 작업을 할당하는 방법에 대한 정보 및 기타 주제의 모든 종류.

어떤 경우에는 원하는 병렬 처리 수준도 지정할 수 있습니다.


단일 코어 머신에서 ... Parallel.ForEach 컬렉션의 여러 파티션 (청크)이 여러 스레드 사이에서 작업하고 있지만이 수는 알고리즘을 기반으로 계산되며이 수는 작업을 지속적으로 모니터링하는 것으로 보입니다. ForEach에 할당하는 스레드입니다. 따라서 ForEach의 본문 부분이 오래 실행되는 IO 바인딩 / 차단 기능을 호출하여 스레드를 대기 상태로두면 알고리즘이 더 많은 스레드를 생성하고 그 사이에 컬렉션을 다시 분할합니다 . 스레드가 빠르게 완료되고 예를 들어 단순히 일부 숫자를 계산하는 것과 같이 IO 스레드를 차단하지 않으면알고리즘은 처리량 (각 반복의 평균 완료 시간)에 대해 알고리즘이 최적이라고 간주하는 지점까지 스레드 수를 늘리거나 줄 입니다.

기본적으로 모든 다양한 병렬 라이브러리 함수 뒤에있는 스레드 풀은 사용할 최적의 스레드 수를 계산합니다. 물리적 프로세서 코어의 수는 방정식의 일부일뿐입니다. 코어 수와 생성 된 스레드 수 사이에는 단순한 일대일 관계가 없습니다.

동기화 스레드의 취소 및 처리에 대한 문서는 매우 유용하지 않습니다. MS가 MSDN에서 더 나은 예제를 제공 할 수 있기를 바랍니다.

잊지 마세요. 본문 코드는 모든 일반적인 스레드 안전 고려 사항과 함께 여러 스레드에서 실행되도록 작성되어야합니다. 프레임 워크는 아직 해당 요소를 추상화하지 않습니다.


프로세서 / 코어 수에 따라 최적의 스레드 수를 계산합니다. 한 번에 모두 스폰되지는 않습니다.


병렬 처리를 참조하십시오 . 반복 당 하나의 태스크를 사용합니까? 사용할 "정신적 모델"에 대한 아이디어. 그러나 저자는 "하루가 끝나면 구현 세부 사항이 언제든지 변경 될 수 있음을 기억하는 것이 중요합니다."라고 말합니다.


좋은 질문입니다. 귀하의 예에서 병렬화 수준은 쿼드 코어 프로세서에서도 매우 낮지 만 일부 대기 상태에서는 병렬화 수준이 상당히 높아질 수 있습니다.

// Max concurrency: 5
[Test]
public void Memory_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);
        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

이제 HTTP 요청을 시뮬레이션하기 위해 대기 작업이 추가되면 어떻게되는지 살펴보십시오.

// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

아직 변경하지 않았으며 동시성 / 병렬화 수준이 급격히 올라갔습니다. 동시성은 ParallelOptions.MaxDegreeOfParallelism.

// Max concurrency: 43
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

// Max concurrency: 391
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(100000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

I reccommend setting ParallelOptions.MaxDegreeOfParallelism. It will not necessarily increase the number of threads in use, but it will ensure you only start a sane number of threads, which seems to be your concern.

Lastly to answer your question, no you will not get all threads to start at once. Use Parallel.Invoke if you are looking to invoke in parallel perfectly e.g. testing race conditions.

// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
    ConcurrentBag<string> monitor = new ConcurrentBag<string>();
    ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(DateTime.UtcNow.Ticks.ToString());
        monitor.TryTake(out string result);
        monitorOut.Add(result);
    });

    var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
    Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}

참고URL : https://stackoverflow.com/questions/1114317/does-parallel-foreach-limit-the-number-of-active-threads

반응형