2013-02-27 5 views
0

큰 텍스트 파일을 읽고 검색어가 포함 된 줄을 검색하는 작은 유틸리티를 작성했습니다. 나는 이것을 TPL Dataflow를 배울 수있는 기회로 삼고있다.작업 실행 실패

검색 용어가 파일의 맨 끝에 있지 않으면 코드가 제대로 작동합니다. 이 경우 uiResult 작업 블록은 이 아니며에 중단 점이 없으면 호출되지 않습니다.

내 이해 데이터 searcher (이것은 데이터의 마지막 블록이 처리 된) 완료된다searcher 후, 에서 uiResult에 게시되어있다. 데이터는 uiResult에 게시되었으므로 해당 데이터가 처리 될 때까지 완료되지 않아야합니다.

질문

uiResult 데이터가 여기에 게시 된 경우에도 완료 될 것입니다 (중단 점은 uiResult 설정하지 않는 한)? 가능한 한 다시 손질로

코드

여기에서 관련 코드입니다 :

ActionBlock<LineInfo> uiResult = new ActionBlock<LineInfo>(li => 
    { 
     // If match found near end of file, the following line only runs 
     // if a breakpoint is set on it: 
     if (results != null) results.Add(li); 
    }, 
    new ExecutionDataflowBlockOptions() 
    { 
     MaxDegreeOfParallelism = 1, 
     CancellationToken = cancelSource.Token, 
     TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext() 
    }); 

BatchBlock<LineInfo> batcher = new BatchBlock<LineInfo>(5000); 

ActionBlock<LineInfo[]> searcher = new ActionBlock<LineInfo[]>(lines => 
    { 
     foreach (LineInfo li in lines) 
     { 
      if (li.TextOfLine.Contains(searchTerm)) 
      { 
       uiResult.Post(li); 
      } 
     } 
    }, 
    new ExecutionDataflowBlockOptions() 
    { 
     MaxDegreeOfParallelism = 1, 
     CancellationToken = cancelSource.Token 
    }); 

batcher.LinkTo(searcher); 

batcher.Completion.ContinueWith(t => 
{ 
    if (t.IsFaulted) ((IDataflowBlock)searcher).Fault(t.Exception); 
    else searcher.Complete(); 

    if (t.IsFaulted) ((IDataflowBlock)uiResult).Fault(t.Exception); 
    else uiResult.Complete(); 
}); 

Task.Run(() => 
    { 
     using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
     using (BufferedStream bs = new BufferedStream(fs)) 
     using (StreamReader sr = new StreamReader(bs)) 
     { 
      string line; 
      while ((line = sr.ReadLine()) != null && cancelSource.IsCancellationRequested == false) 
      { 
       batcher.Post(new LineInfo() { LineNumber = lineNumber, OffsetOfLine = offset, TextOfLine = line }); 
      } 

      batcher.Complete(); 
      try 
      { 
       searcher.Completion.Wait(); 
       uiResult.Completion.Wait(); 
      } 
      catch (AggregateException ae) 
      { 
       TaskCanceledException taskCancelled = ae.InnerException as TaskCanceledException; 
       if (taskCancelled != null) 
       { 
        // Swallow the Exception if is just a user cancellation 
        throw; 
       } 
      } 
      finally 
      { 
       signalDone(); 
      } 
     } 
    }); 
+0

정말로 중단 점을 설정하면 동작이 변경되는 것입니까? 그것은 매우 의심 스럽습니다. – svick

+0

@svick : 그래, 그 행동을 일관되게 재현 할 수 있습니다. 중단 점에 의해 변경되는 스레드 타이밍 문제가있는 것처럼 느껴집니다. 필자가 이해하는 바는 작성된 코드가 결정 론적이어야한다는 것입니다. –

답변

1

귀하의 코드가 있기 때문에 당신이 완료를 처리하고있는 방식의 비 결정적이다.

  1. Task 프로세스 전체 파일 및 batcherComplete()를 호출하여 이벤트의 가능한 순서는 이것이다.
  2. batcher은 마지막 배치를 처리하여 searcher으로 전송하고 완료합니다.
  3. uiResult에서 Complete()을 호출하는 연속이 실행됩니다.
  4. uiResult에는 할 일이 없으므로 완료됩니다.
  5. searcher은 각 결과를 uiResult으로 보내려고 마지막 배치를 처리합니다. 그러나 uiResult은 이미 완료되었으므로 모든 것을 거부합니다. 즉, Post()false을 반환하지만이를 확인하지는 않습니다.

그래서 문제는 이미 완료된 블록으로 무언가를 보내려고한다는 것입니다. 이는 효과가 없습니다.

해결책은 완료되기 전에 블록 (즉, Completion이 완료 됨) 이후에만 블록에 Complete()을 호출하는 것입니다. 아마도 가장 쉬운 방법은 PropagateCompletionLinkTo()과 함께 사용하는 것입니다.

+0

이제 문제를 지적했지만 해결 방법을 이해하지 못했습니다. PropagateCompletion에 대한 설명서는 약간 밝습니다. 'batcher.Completion '주위에서 어떻게 코드를 재구성 할 것인가?ContinueWith'는 오류를 올바르게 처리하지만'searcher'와'uiResult'가 기존 작업을 끝낼 때까지 기다려야합니까? –

+0

@EricJ. 내가 말했듯이'ContinueWith()'를 사용하지 말고 대신'PropagateCompletion'을 사용하십시오. 그러면 다음 블록이 올바르게 완료되거나 오류가 발생할 때 오류가 발생합니다. 'COntinueWith()'를 계속 사용하려면'searcher'를 완료하는'batcher.Completion'과'uiResult'를 완료하는'searcher.Completion'을위한 두 개의 연속이 필요합니다. – svick

+0

고맙습니다. 제 문제를 해결했습니다. 내가 이해할 수 있듯이 ... searcher와 uiResult는 모두 ActionBlock 이다. 따라서 LinkTo를 사용할 수 없다. (둘 다'IPropagatorBlock '을 구현하지 않는다.) 나는 batcher-> searcher 링크와 함께 'PropogateCompletion'을 사용하여이 문제를 해결했습니다. 또한'searcher'를'uforms'에 연결된'TransformManyBlock '으로 변경하고 빈 열거 형 (검색과 일치하지 않는 라인의 경우) 또는 1 요소의 열거 (일치하는 라인의 경우) 중 하나를 반환 할 수 있습니까? –