3

의 끝 부분에 각각 정규처럼 행동 : 500 개 레코드가 있다고 가정Parallel.ForEach 나는 이런 식으로 뭔가 실행했을 때 나는이 문제가 발생하고 반복

Parallel.ForEach(dataTable.AsEnumerable(), row => 
{ 
    //do processing 
} 

는 병렬 일단 (870) 말 .ForEach 850 완료, 그것은 순차적으로 즉, 한 번에 하나의 작업을 실행하는 것 같습니다. 그것은 850 개의 작업을 매우 빠르게 끝 냈지만 반복의 끝에 가까워지면 속도가 매우 느려지고 각각의 작업처럼 규칙적인 것처럼 보입니다. 나는 심지어 2000 기록을 시도했다.

내 코드에 문제가 있습니까? 제안을하십시오. 다음은

내가

미안 난 그냥 잘못된 예를 게시 사용하고있는 코드입니다.

Task newTask = Task.Factory.StartNew(() => 
{ 
    Parallel.ForEach(dtResult.AsEnumerable(), dr => 
    { 
     string extractQuery = ""; 
     string downLoadFileFullName = ""; 
     lock (foreachObject) 
     { 

      string fileName = extractorConfig.EncodeFileName(dr); 
      extractQuery = extractorConfig.GetExtractQuery(dr); 
      if (string.IsNullOrEmpty(extractQuery)) throw new Exception("Extract Query not found. Please check the configuration"); 

      string newDownLoadPath = CommonUtil.GetFormalizedDataPath(sDownLoadPath, uKey.CobDate); 
      //create folder if it doesn't exist 
      if (!Directory.Exists(newDownLoadPath)) Directory.CreateDirectory(newDownLoadPath); 
      downLoadFileFullName = Path.Combine(newDownLoadPath, fileName); 
     } 
     Interlocked.Increment(ref index); 

     ExtractorClass util = new ExtractorClass(SourceDbConnStr); 
     util.LoadToFile(extractQuery, downLoadFileFullName); 
     Interlocked.Increment(ref uiTimerIndex); 
    }); 
}); 
+3

전체 코드 블록을 제공하십시오 –

+2

이것이 주석이나 대답이되어야할지 모르겠지만 지적해야 할 필요가 있습니다 :'DataTable'은 thread-safe 타입이 아닙니다. 그래서 만약 당신의'// do do'' 코드가 어떤 종류의 변형 (모든 행의 셀까지)을 포함한다면, 여러분은 고통의 세계를 요구하고 있습니다, 나는 두려워합니다. –

+0

DataTable의 모든 행에 대해 데이터베이스를 호출하여 데이터를 가져 와서 파일로로드합니다. 그것은 추출 과정과 같습니다. 데이터베이스에서 데이터를 가져 와서 파일로 추출합니다. –

답변

3

내 생각 엔 :

이 잠재적 IO의 높은 수준이 보인다

:

  • 데이터베이스를 + 디스크
  • DB에
  • 네트워크 통신 및 디스크
  • 다시
  • 작성 결과

따라서 IO를 기다리는 데 많은 시간이 소요됩니다. 내 생각 엔 더 많은 스레드가 믹스에 추가되고 IO가 더욱 강조됨에 따라 대기가 점점 악화되고 있다는 것입니다. 예를 들어 디스크에는 헤드 세트가 하나뿐이므로 동시에 기록 할 수 없습니다. 동시에 쓰려고하는 스레드 수가 많으면 성능이 저하됩니다. 스레드의 최대 수를 제한

에 한번 사용됩니다

    :

    var options = new ParallelOptions { MaxDegreeOfParallelism = 2 }; 
    
    Parallel.ForEach(dtResult.AsEnumerable(), options, dr => 
    { 
        //Do stuff 
    }); 
    

    업데이트

    코드 편집 후, 나는 변화 몇 가지는 다음과 제안

  • 최대 스레드 수 줄이기 -이 방법을 실험 해 볼 수 있습니다.
  • 디렉토리 검사 및 생성은 한 번만 수행하십시오.

코드 :

private static bool isDirectoryCreated; 

//... 

var options = new ParallelOptions { MaxDegreeOfParallelism = 2 }; 

Parallel.ForEach(dtResult.AsEnumerable(), options, dr => 
{ 
    string fileName, extractQuery, newDownLoadPath; 

    lock (foreachObject) 
    { 
     fileName = extractorConfig.EncodeFileName(dr); 

     extractQuery = extractorConfig.GetExtractQuery(dr); 

     if (string.IsNullOrEmpty(extractQuery)) 
      throw new Exception("Extract Query not found. Please check the configuration"); 

     newDownLoadPath = CommonUtil.GetFormalizedDataPath(sDownLoadPath, uKey.CobDate); 

     if (!isDirectoryCreated) 
     { 
      if (!Directory.Exists(newDownLoadPath)) 
       Directory.CreateDirectory(newDownLoadPath); 

      isDirectoryCreated = true; 
     } 
    } 

    string downLoadFileFullName = Path.Combine(newDownLoadPath, fileName); 

    Interlocked.Increment(ref index); 

    ExtractorClass util = new ExtractorClass(SourceDbConnStr); 
    util.LoadToFile(extractQuery, downLoadFileFullName); 

    Interlocked.Increment(ref uiTimerIndex); 
}); 
+0

@chibacity 나는 제안을 시도했다. 그 같은 실행을 추측하지만, 코드에서 수정을 가리키는 주셔서 감사합니다. –

+0

@bunny 그래서 스레드 수를 제한하면 개선되지 않았습니까? –

+0

@chibacity 실제로 조금 느려졌습니다. –

2

그것은 관련 코드가없는 세부 사항을 제공하기 어렵다 그러나 일반적으로이 예상되는 동작입니다 : 이것은 올바른 코드입니다. .NET은 모든 프로세서가 고르게 작업하도록 일정을 계획합니다.

그러나이 작업은 모든 작업이 동일한 시간이 걸리는 것은 아니므로 근사치 수 있습니다. 결국 일부 프로세서는 작동 할 것이고 어떤 프로세서는 작동하지 않을 것이고, 작업을 재배포하는 것은 값 비싸지 만 항상 유익한 것은 아닙니다.

PLinq에서 사용하는로드 균형 조정에 대한 세부적인 내용은 알지 못하지만 결론은이 동작을 완전히 방지 할 수 없다는 것입니다.

1

당신이 두 개의 스레드에 병렬 처리를 제한하는 것으로 가정합니다. Parallel.ForEach이 잠재적으로 작동 할 수있는 두 가지 가능한 방법이 있습니다. 한 가지 방법은 두 개의 스레드가 시작되고 각 스레드에 완료 할 항목의 절반이 제공된다는 것입니다. 따라서 850 개의 항목이있는 경우 실제로 스레드 1에는 첫 번째 425 개 항목이 주어지고 스레드 2에는 두 번째 425 개 항목이 제공됩니다. 이제 두 스레드가 모두 작동합니다. 처리 된 항목의 순서는 [0, 425, 426, 1, 2, 427, 3, 428, 429, 4, ...]와 유사합니다.

스레드 중 하나가 다른 스레드보다 훨씬 빠르게 항목 그룹을 완성 할 가능성이 매우 높습니다 (사실).

또 다른 방법은 두 개의 스레드를 시작하고 각 스레드가 목록에서 항목을 가져 와서 처리 한 다음 처리 할 항목이 없을 때까지 다음 항목을 가져 오는 것입니다. 이 경우 처리되는 항목의 순서는 [0, 1, 2, 4, 3, 6, 5, ...]와 비슷합니다.

첫 번째 예에서 각 스레드는 처리 할 항목 블록을 제공받습니다. 두 번째 경우에는 각 스레드가 남아있는 항목이 없을 때까지 공통 블록의 항목을 처리합니다.

여러 가지가 있지만 여러 스레드간에 작업을 분할하는 두 가지 주요 방법입니다. 각 항목에 고유 한 항목 그룹을 지정하거나 각 스레드가 처리를 마친 다음 항목을 요청할 것으로 기대하십시오.

Parallel.ForEach은 첫 번째 방법으로 구현됩니다. 각 스레드는 처리 할 항목 그룹을 제공받습니다. 다른 방법을 사용하면 항목 목록이 결과적으로 동기화 오버 헤드와 함께 공유 대기열처럼 취급되어야하기 때문에 더 많은 오버 헤드가 필요합니다.

+1

'Parallel.ForEach'는 제안한 방식대로 작동하지 않습니다. 즉, 입력 목록을 분할 한 다음 처음부터 전용 스레드로 나눕니다. 사실 두 번째 방법으로 작동합니다. 작업 대기열이 있으며 스레드가 작업 대기열을 통과합니다. 'MaxDegreeOfParallelism = 2'를 지정하면 처리 수명이 다할 때까지 3 가지 이상의 스레드를 얻을 가능성이 높습니다.하지만 한 번에 2 개의 실행 만있을 것입니다. 실제 순서는 두 번째 예, 즉 [0, 1, 2, 4, 3, 6, 5, ...]입니다. 그것은 세밀하고 interleaved입니다. –

+1

@chibacity : 정보 주셔서 감사합니다. 흥미 롭 군. 그것은 실험에서 얻은 결과와 모순됩니다. 나는 내가 어디로 잘못 갔는지보아야 할 것이다. –

+1

예 : public void test() { var opts = new ParallelOptions {MaxDegreeOfParallelism = 2}; var r = 새 목록 (); INT t = Thread.CurrentThread.ManagedThreadId; 로크 (R) r.Add (새로운 결과 Parallel.ForEach (Enumerable.Range (0, 1000), I => { Thread.sleep를 (1)의 opts {Th = t, I = i}), }); 012.TheList() .ForEach (x => Console.WriteLine ("Th : {0,2}, I : {1}.", x.Th, x.I)); } 공용 클래스 결과 { public int I, Th; } –

관련 문제