2016-05-05 19 views
2

テストWPFプロジェクトTPLデータフローを使用して特定の親ディレクトリのすべてのサブディレクトリを列挙し、特定のファイル拡張子を持つファイルのリストを作成しようとしています。 ".xlsx"最初のdirToFilesBlockと最後のfileActionBlockの2つのブロックを使用します。再帰を伴うTPLデータフロー

すべてのサブディレクトリを通過する再帰的効果を作成するには、最初のブロックに、リンク述語テストを使用して、出力項目がディレクトリであるかどうかを確認するリンクがあります。これは私が非同期プログラミングに関する本で見つけたアプローチです。 2番目のリンクはfileActionBlockへのリンクで、ファイルを正しい拡張子で調べるリンク述部テストに基づいて、ファイルをリストに追加します。

私が抱えている問題は、btnStart_Clickで物事を蹴った後に終了しないことです。つまり、「完了」メッセージを表示するために、イベントハンドラの待機時間を下回ることはありません。私はおそらくdirToFilesBlock.Complete()を呼び出す必要があることを理解していますが、コード内でどこがどのような条件であるべきかわかりません。私はそれがサブディレクトリを与えることからリンクを戻すのを止めるので、最初の投稿の後でそれを呼び出すことはできません。私はInputCountとOutputCountのプロパティを使って作業を試みましたが、それほど遠くには達しませんでした。できるだけデータフローの構造をそのままにしておきたいと思っています。つまり、リンクを介して探索される新しいディレクトリごとにUIを更新し、ユーザーに進捗状況をフィードバックすることができます。

私はTPLデータフローに非常に慣れていて、どんな助けもありがとうございます。ここで

は、分離コードファイルからのコードです:

public partial class MainWindow : Window 
{ 
    TransformManyBlock<string, string> dirToFilesBlock; 
    ActionBlock<string> fileActionBlock; 
    ObservableCollection<string> files; 
    CancellationTokenSource cts; 
    CancellationToken ct; 
    public MainWindow() 
    { 
     InitializeComponent(); 

     files = new ObservableCollection<string>(); 

     lst.DataContext = files; 

     cts = new CancellationTokenSource(); 
     ct = cts.Token; 
    } 

    private Task Start(string path) 
    { 
     var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

     dirToFilesBlock = new TransformManyBlock<string, string>((Func<string, IEnumerable<string>>)(GetFileSystemItems), new ExecutionDataflowBlockOptions() { CancellationToken = ct }); 
     fileActionBlock = new ActionBlock<string>((Action<string>)ProcessFile, new ExecutionDataflowBlockOptions() {CancellationToken = ct, TaskScheduler = uiScheduler}); 

     // Order of LinkTo's important here! 
     dirToFilesBlock.LinkTo(dirToFilesBlock, new DataflowLinkOptions() { PropagateCompletion = true }, IsDirectory); 
     dirToFilesBlock.LinkTo(fileActionBlock, new DataflowLinkOptions() { PropagateCompletion = true }, IsRequiredDocType); 

     // Kick off the recursion. 
     dirToFilesBlock.Post(path); 

     return Task.WhenAll(dirToFilesBlock.Completion, fileActionBlock.Completion); 
    } 

    private bool IsDirectory(string path) 
    { 

     return Directory.Exists(path); 
    } 


    private bool IsRequiredDocType(string fileName) 
    { 
     return System.IO.Path.GetExtension(fileName) == ".xlsx"; 
    } 

    private IEnumerable<string> GetFilesInDirectory(string path) 
    { 
     // Check for cancellation with each new dir. 
     ct.ThrowIfCancellationRequested(); 

     // Check in case of Dir access problems 
     try 
     { 
      return Directory.EnumerateFileSystemEntries(path); 
     } 
     catch (Exception) 
     { 
      return Enumerable.Empty<string>(); 
     } 
    } 

    private IEnumerable<string> GetFileSystemItems(string dir) 
    { 
     return GetFilesInDirectory(dir); 
    } 

    private void ProcessFile(string fileName) 
    { 
     ct.ThrowIfCancellationRequested(); 

     files.Add(fileName); 
    } 

    private async void btnStart_Click(object sender, RoutedEventArgs e) 
    { 
     try 
     { 
      await Start(@"C:\"); 
      // Never gets here!!! 
      MessageBox.Show("Completed"); 

     } 
     catch (OperationCanceledException) 
     { 
      MessageBox.Show("Cancelled"); 

     } 
     catch (Exception) 
     { 
      MessageBox.Show("Unknown err"); 
     } 
     finally 
     { 
     } 
    } 

    private void btnCancel_Click(object sender, RoutedEventArgs e) 
    { 
     cts.Cancel(); 
    } 
} 

}

答えて

1

これは古い質問ですが、データフローループ内で完了を処理することは、まだ問題になることができます。

あなたの場合、TransfomBlockには飛行中のアイテムの数を保持させることができます。これは、ブロックが任意の数の項目を処理するためにビジーであることを示します。ブロックがビジーでなく、両方のバッファが空の場合にのみ、Complete()に電話します。処理完了の詳細については、私が書いた投稿でFinding Completion in a Complex Flow: Feedback Loops

public partial class MainWindow : Window { 

     TransformManyBlock<string, string> dirToFilesBlock; 
     ActionBlock<string> fileActionBlock; 
     ObservableCollection<string> files; 
     CancellationTokenSource cts; 
     CancellationToken ct; 
     public MainWindow() { 
      InitializeComponent(); 

      files = new ObservableCollection<string>(); 

      lst.DataContext = files; 

      cts = new CancellationTokenSource(); 
      ct = cts.Token; 
     } 

     private async Task Start(string path) { 
      var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

      dirToFilesBlock = new TransformManyBlock<string, string>((Func<string, IEnumerable<string>>)(GetFileSystemItems), new ExecutionDataflowBlockOptions() { CancellationToken = ct }); 
      fileActionBlock = new ActionBlock<string>((Action<string>)ProcessFile, new ExecutionDataflowBlockOptions() { CancellationToken = ct, TaskScheduler = uiScheduler }); 

      // Order of LinkTo's important here! 
      dirToFilesBlock.LinkTo(dirToFilesBlock, new DataflowLinkOptions() { PropagateCompletion = true }, IsDirectory); 
      dirToFilesBlock.LinkTo(fileActionBlock, new DataflowLinkOptions() { PropagateCompletion = true }, IsRequiredDocType); 

      // Kick off the recursion. 
      dirToFilesBlock.Post(path); 

      await ProcessingIsComplete(); 
      dirToFilesBlock.Complete(); 
      await Task.WhenAll(dirToFilesBlock.Completion, fileActionBlock.Completion); 
     } 

     private async Task ProcessingIsComplete() { 
      while (!ct.IsCancellationRequested && DirectoryToFilesBlockIsIdle()) { 
       await Task.Delay(500); 
      } 
     } 

     private bool DirectoryToFilesBlockIsIdle() { 
      return dirToFilesBlock.InputCount == 0 && 
       dirToFilesBlock.OutputCount == 0 && 
       directoriesBeingProcessed <= 0; 
     } 

     private bool IsDirectory(string path) { 
      return Directory.Exists(path); 
     } 


     private bool IsRequiredDocType(string fileName) { 
      return System.IO.Path.GetExtension(fileName) == ".xlsx"; 
     } 

     private int directoriesBeingProcessed = 0; 

     private IEnumerable<string> GetFilesInDirectory(string path) { 
      Interlocked.Increment(ref directoriesBeingProcessed) 
      // Check for cancellation with each new dir. 
      ct.ThrowIfCancellationRequested(); 

      // Check in case of Dir access problems 
      try { 
       return Directory.EnumerateFileSystemEntries(path); 
      } catch (Exception) { 
       return Enumerable.Empty<string>(); 
      } finally { 
       Interlocked.Decrement(ref directoriesBeingProcessed); 
      } 
     } 

     private IEnumerable<string> GetFileSystemItems(string dir) { 
      return GetFilesInDirectory(dir); 
     } 

     private void ProcessFile(string fileName) { 
      ct.ThrowIfCancellationRequested(); 

      files.Add(fileName); 
     } 

     private async void btnStart_Click(object sender, RoutedEventArgs e) { 
      try { 
       await Start(@"C:\"); 
       // Never gets here!!! 
       MessageBox.Show("Completed"); 

      } catch (OperationCanceledException) { 
       MessageBox.Show("Cancelled"); 

      } catch (Exception) { 
       MessageBox.Show("Unknown err"); 
      } finally { 
      } 
     } 

     private void btnCancel_Click(object sender, RoutedEventArgs e) { 
      cts.Cancel(); 
     } 
    } 
関連する問題