fork download
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9.  
  10. using System.IO;
  11.  
  12. namespace DirectoryTask
  13. {
  14. class DirectoryTaskImpl : IDirectoryTask
  15. {
  16. int folders;
  17. int folderTotal;
  18. int files;
  19. int fileTotal;
  20.  
  21. object sync = new object();
  22. public bool Cancelation{ get; private set; }
  23.  
  24. Func<CancellationTokenSource> getSource;
  25. Action<CancellationTokenSource> setSource;
  26.  
  27. /// <summary>
  28. /// TokenSourceをこちらでコントロールしたいがinterfaceは変えられないのでこのような奇形設計に
  29. /// </summary>
  30. public DirectoryTaskImpl(Func<CancellationTokenSource> getSource, Action<CancellationTokenSource> setSource)
  31. {
  32. this.getSource = getSource;
  33. this.setSource = setSource;
  34. }
  35.  
  36. void Reset()
  37. {
  38. folders = folderTotal = files = fileTotal = 0;
  39. ShowProgress();
  40. setSource(new CancellationTokenSource());
  41. Cancelation = false;
  42. }
  43.  
  44. /// <summary>
  45. /// pathがファイルならそのファイルを、フォルダなら直下のファイルを処理するStart済みのTaskが返る
  46. /// キャンセルや例外により停止している時にはnullが返る
  47. /// </summary>
  48. public Task Entry(string path, CancellationToken token)
  49. {
  50. //キャンセル中でもnullが返らない場合がありうるが
  51. //今のところエラーメッセージが出ないぐらいの影響しかない
  52. if (Cancelation) return null;
  53.  
  54. if (Directory.Exists(path))
  55. {
  56. //フォルダならその下にあるファイルを処理する
  57. Task r = new Task(() =>
  58. {
  59. //キャンセル中なのに入ってしまった場合は無駄な処理を起動しないようにする
  60. if (token.IsCancellationRequested) return;
  61.  
  62. Interlocked.Increment(ref folders);
  63. Interlocked.Increment(ref folderTotal);
  64.  
  65. var tasks = Directory.GetFiles(path).Select(p => TaskStart(p, token)).ToArray();
  66.  
  67. try
  68. {
  69. Task.WaitAll(tasks);
  70. }
  71. //tasksのどれかがキャンセルなり失敗したらAggregateExceptionが飛ぶ
  72. catch (AggregateException) { return; }
  73.  
  74. Interlocked.Decrement(ref folders);
  75. });
  76. r.Start();
  77. return r;
  78. }
  79. else
  80. {
  81. //ファイルならそれを処理する
  82. Task r = TaskStart(path, token);
  83. return r;
  84. }
  85. }
  86.  
  87.  
  88. Task TaskStart(string path, CancellationToken token)
  89. {
  90. var r = new Task(() => Hoge(path, token), token);
  91. r.ContinueWith(task => Finish(task, token));
  92. Interlocked.Increment(ref files);
  93. Interlocked.Increment(ref fileTotal);
  94. ShowProgress();
  95. r.Start();
  96. return r;
  97. }
  98.  
  99. void Finish(Task t, CancellationToken token)
  100. {
  101. //Completeが複数回呼ばれないようにロックする
  102. lock (sync)
  103. {
  104. Interlocked.Decrement(ref files);
  105.  
  106. if (Cancelation)
  107. {
  108. //キャンセルした後もFinishが呼ばれ続ける
  109. //それが全部終われば上のDecrementにより最終的にfilesが0になりResetが入る
  110. if (files == 0) Reset();
  111. return;
  112. }
  113.  
  114. if (t.IsCanceled)
  115. {
  116. Cancelation = true;
  117. DoComplete(ExitReason.Cancel);
  118. }
  119. else if (t.IsFaulted)
  120. {
  121. //例外が起きた場合
  122. Cancelation = true;
  123. getSource().Cancel();
  124. //AggregateExceptionが返る。これは複数のInnerExceptionを取れるようになってるが、この局面では多分一個だけのはず
  125. ExceptionThrown(null, new ExceptionEventArgs { Exception = t.Exception.InnerExceptions[0] });
  126. DoComplete(ExitReason.Fatal);
  127. }
  128. else if (t.IsCompleted)
  129. {
  130. if (files == 0)
  131. {
  132. //全部処理し終わった
  133. DoComplete(ExitReason.Complete);
  134. Reset();
  135. }
  136. else
  137. {
  138. ShowProgress();
  139. }
  140. }
  141. else throw new Exception("???");
  142. }
  143. }
  144.  
  145. void DoComplete(ExitReason er)
  146. {
  147. Complete(this, new ExitEventArgs { Reason = er });
  148. }
  149.  
  150. void ShowProgress()
  151. {
  152. FileProgress(null, new ProgressEventArgs { Done = fileTotal - files, Total = fileTotal });
  153. FolderProgress(null, new ProgressEventArgs { Done = folderTotal - folders, Total = folderTotal });
  154. }
  155.  
  156. static Random Rand = new Random();
  157.  
  158. /// <summary>
  159. /// なんか適当な処理
  160. /// </summary>
  161. void Hoge(string path, CancellationToken token)
  162. {
  163. //無駄な処理をしないようにしておく
  164. if (token.IsCancellationRequested) return;
  165.  
  166. Thread.Sleep(1000*(Rand.Next(2) + 1));
  167.  
  168. //500回に1回例外が起こるようにしてみた
  169. if(Rand.Next(500) == 0)
  170. throw new Exception("Exceptionだ!");
  171. }
  172.  
  173. public event EventHandler<ExitEventArgs> Complete;
  174. public event EventHandler<ExceptionEventArgs> ExceptionThrown;
  175. public event EventHandler<ProgressEventArgs> FileProgress;
  176. public event EventHandler<ProgressEventArgs> FolderProgress;
  177. }
  178.  
  179.  
  180. }
  181.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty