Programing

IO 바인딩 작업에 ThreadPools 또는 Task Parallel Library를 사용해야합니까?

lottogame 2020. 10. 17. 09:00
반응형

IO 바인딩 작업에 ThreadPools 또는 Task Parallel Library를 사용해야합니까?


애그리 게이터 인 내 프로젝트 중 하나에서 웹에서 피드, 팟 캐스트 등을 구문 분석합니다.

순차 접근 방식을 사용하면 리소스가 많기 때문에 모든 리소스를 처리하는 데 상당한 시간이 걸립니다 (네트워크 문제 및 유사한 문제로 인해).

foreach(feed in feeds)
{
   read_from_web(feed)
   parse(feed)
}

그래서 동시성을 구현하고 싶고 기본적으로 ThreadPools를 사용하여 작업자 스레드로 처리할지 아니면 TPL에 의존하여 정렬할지 결정할 수 없었습니다.

ThreadPools는 확실히 작업자 스레드로 작업을 처리하고 내가 기대하는 바를 얻을 수 있습니다 (멀티 코어 CPU 환경에서는 다른 코어도 활용됩니다).

동시성

그래도 TPL도 추천 방법으로 고려하고 싶지만 조금 걱정이됩니다. 우선 TPL이 ThreadPools를 사용하지만 의사 결정의 추가 계층을 추가한다는 것을 알고 있습니다. 저는 주로 단일 코어 환경이 존재하는 상황에 대해 걱정합니다. 내가 틀리지 않았다면 TPL은 처음에 사용 가능한 CPU 코어 수와 동일한 작업자 스레드 수로 시작됩니다. TPL이 내 IO 바인딩 사례에 대해 순차적 접근 방식과 유사한 결과를 생성하는 것이 두렵습니다.

따라서 IO 바인딩 작업 (내 경우에는 웹에서 리소스 읽기)의 경우 ThreadPools를 사용하고 사물을 제어하는 ​​것이 가장 좋을까요, 아니면 TPL에 의존하는 것이 더 좋을까요? IO 바인딩 시나리오에서도 TPL을 사용할 수 있습니까?

업데이트 : 내 주요 관심사는 단일 코어 CPU 환경에서 TPL이 순차적 접근 방식처럼 작동합니까 아니면 여전히 동시성을 제공합니까? 나는 이미 Microsoft .NET으로 Parallel Programming을 읽고 있으므로 책을 읽고 있지만 이에 대한 정확한 답을 찾을 수 없습니다.

참고 : 이것은 이전 질문을 다시 표현한 것입니다 [ 스레드 동시성과 병렬성을 함께 사용할 수 있습니까? ] 꽤 잘못된 표현이었습니다.


그래서 나는 대신 이것에 대한 테스트를 작성하고 실제 데이터에서 확인하기로 결정했습니다.

테스트 범례

  • Itr : 반복
  • Seq : 순차적 접근.
  • PrlEx : 병렬 확장-Parallel.ForEach
  • TPL : 작업 병렬 라이브러리
  • TPool : ThreadPool

시험 결과

싱글 코어 CPU [Win7-32]-VMWare에서 실행-

Test Environment: 1 physical cpus, 1 cores, 1 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.82s  04.05s  02.69s  02.60s
#2      07.48s  03.18s  03.17s  02.91s
#3      07.66s  03.21s  01.90s  01.68s
#4      07.43s  01.65s  01.70s  01.76s
#5      07.81s  02.20s  01.75s  01.71s
#6      07.67s  03.25s  01.97s  01.63s
#7      08.14s  01.77s  01.72s  02.66s
#8      08.04s  03.01s  02.03s  01.75s
#9      08.80s  01.71s  01.67s  01.75s
#10     10.19s  02.23s  01.62s  01.74s
________________________________________________________________________________

Avg.    08.40s  02.63s  02.02s  02.02s
________________________________________________________________________________

싱글 코어 CPU [WinXP]-VMWare에서 실행-

Test Environment: 1 physical cpus, NotSupported cores, NotSupported logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.79s  04.05s  02.75s  02.13s
#2      07.53s  02.84s  02.08s  02.07s
#3      07.79s  03.74s  02.04s  02.07s
#4      08.28s  02.88s  02.73s  03.43s
#5      07.55s  02.59s  03.99s  03.19s
#6      07.50s  02.90s  02.83s  02.29s
#7      07.80s  04.32s  02.78s  02.67s
#8      07.65s  03.10s  02.07s  02.53s
#9      10.70s  02.61s  02.04s  02.10s
#10     08.98s  02.88s  02.09s  02.16s
________________________________________________________________________________

Avg.    08.46s  03.19s  02.54s  02.46s
________________________________________________________________________________

듀얼 코어 CPU [Win7-64]

Test Environment: 1 physical cpus, 2 cores, 2 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      07.09s  02.28s  02.64s  01.79s
#2      06.04s  02.53s  01.96s  01.94s
#3      05.84s  02.18s  02.08s  02.34s
#4      06.00s  01.43s  01.69s  01.43s
#5      05.74s  01.61s  01.36s  01.49s
#6      05.92s  01.59s  01.73s  01.50s
#7      06.09s  01.44s  02.14s  02.37s
#8      06.37s  01.34s  01.46s  01.36s
#9      06.57s  01.30s  01.58s  01.67s
#10     06.06s  01.95s  02.88s  01.62s
________________________________________________________________________________

Avg.    06.17s  01.76s  01.95s  01.75s
________________________________________________________________________________

쿼드 코어 CPU [Win7-64]-HyprerThreading 지원-

Test Environment: 1 physical cpus, 4 cores, 8 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________

Itr.    Seq.    PrlEx   TPL     TPool
________________________________________________________________________________

#1      10.56s  02.03s  01.71s  01.69s
#2      07.42s  01.63s  01.71s  01.69s
#3      11.66s  01.69s  01.73s  01.61s
#4      07.52s  01.77s  01.63s  01.65s
#5      07.69s  02.32s  01.67s  01.62s
#6      07.31s  01.64s  01.53s  02.17s
#7      07.44s  02.56s  02.35s  02.31s
#8      08.36s  01.93s  01.73s  01.66s
#9      07.92s  02.15s  01.72s  01.65s
#10     07.60s  02.14s  01.68s  01.68s
________________________________________________________________________________

Avg.    08.35s  01.99s  01.75s  01.77s
________________________________________________________________________________

요약

  • 단일 코어 환경에서 실행하든 멀티 코어 환경에서 실행하든 Parallel Extensions, TPL 및 ThreadPool 은 동일하게 작동하고 대략적인 결과를 제공합니다 .
  • 여전히 TPL 에는 쉬운 예외 처리, 취소 지원 및 작업 결과를 쉽게 반환하는 기능과 같은 장점 이 있습니다 . Parallel Extensions도 또 다른 실행 가능한 대안입니다.

스스로 테스트 실행

여기 에서 소스를 다운로드하고 직접 실행할 수 있습니다 . 결과를 게시 할 수 있으면 나도 추가하겠습니다.

업데이트 : 소스 링크를 수정했습니다.


If you're trying to maximize throughput for IO-bound tasks you absolutely must combine the traditional Asynchronous Processing Model (APM) APIs with your TPL based work. The APM APIs are the only way to unblock the CPU thread whilst the asynchronous IO callback is pending. The TPL provides the TaskFactory::FromAsync helper method to assist in combining APM and TPL code.

Check out this section of the .NET SDK on MSDN entitled TPL and Traditional .NET Asynchronous Programming for more information on how to combine these two programming models to achieve async nirvana.


You are right that the TPL does remove some of the control you have when you create your own thread pool. But this is only correct if you do not want to dig deeper. The TPL does allow you to create long running Tasks that are not part of the TPL thread pool and could serve your purpose well. The published book which is a free read Parallel Programming with Microsoft .NET will give you much more insight how the TPL is meant to be used. You have always the option to give Paralle.For, Tasks explicit parameters how many threads should be allocated. Besides this you can replace the TPL scheduler with your own one if your want full control.


You can assign your own task scheduler to a TPL task. The default work stealing one is quite clever though.


I do fear of TPL producing similar results to sequential approach for my IO-bound case.

I think it will. What is the bottleneck? Is is parsing or downloading? Multithreading will not help you much with downloading from the web.

I would use Task Parallel Library for cropping, applying mask or effects for downloaded images, cuting some sample from podcast etc. It's more scalable.

But it will not be the order of magnitude speed up. Spend your resources to implementing some features, testing.

PS. "Wow my function execustes in 0.7 s instead of 0.9" ;)


If you parallelize your calls to the urls, I think it will improve your application, even if have only one core. Take a look on this code:

var client = new HttpClient();
var urls = new[]{"a", "url", "to", "find"};

// due to the EAP pattern, this will run in parallel.
var tasks = urls.Select(c=> client.GetAsync(c));

var result = Tasks.WhenAll(task).ContinueWith(a=> AnalyzeThisWords(a.Result));
result.Wait(); // don't know if this is needed or it's correct to call wait

The difference between multithreading and asynchrony in this case is how the callback/completion is done.

When using EAP the number of tasks is not related with the number of threads.

As you're relying on the GetAsync task, the http client uses a networkstream (socket, tcp client or whatever) and signalize it to raise an event when the BeginRead/EndRead is done. So, no threads are involved in this moment.

After the completion is called, maybe a new thread is created, but it's up to TaskScheduler (used in call GetAsync/ContinueWith call) to create a new thread, use an existing thread or inline the task to use the calling thread.

If the AnalyzeThisWords blocks for too much time, then you start to get bottlenecks as the "callback" on the ContinueWith is done from a thread pool worker.

참고URL : https://stackoverflow.com/questions/5213695/should-i-use-threadpools-or-task-parallel-library-for-io-bound-operations

반응형