並列スレッドテスト

デバッガーでは、正しいポイントで実行フローを簡単にキャッチし、分析後に再起動できます。 自動テストでは、これらの操作はめちゃくちゃ複雑に見えます。



そして、なぜそれが必要なのですか?



並列システムの構築は最も簡単なことではありません。 並列処理と同期処理のバランスを維持する必要があります。 同期しないと、安定性が失われます。 再同期すると、一貫したシステムが得られます。



リファクタリングは、一般的に地雷原を歩くことです。



実装の複雑さがなければ、自動テストが大いに役立つ可能性があります。 私の意見では、並列システムでは、コードの個々の競合するセクションの同時動作-事実と実行順序を自動的にチェックするとよいでしょう。



そこで、ようやくスレッドの中断と再開に至りました。 すでに述べたように、このような非自動メカニズムが存在します。 これらはブレークポイントです。 新しい用語、つまりブレークポイント、次にブレークポイントを発明しません。



public class Breakpoint { [Conditional("DEBUG")] public static void Define(string name){…} } public class BreakCtrl : IDisposable { public string Name { get; private set; } public BreakCtrl(string name) {…} public BreakCtrl From(params Thread[] threads) {…} public void Dispose(){…} public void Run(Thread thread){…} public void Wait(Thread thread){…} public bool IsCapture(Thread thread){…} public Thread AnyCapture(){…} }
      
      







自動ブレークポイントのプロパティ:
  1. デバッグモードでのみ機能します(特定のマクロDEBUGを使用)。 追加のコードがエンドユーザーのシステムの動作に影響するとは考えないでください。
  2. ブレークポイントは、そのコントローラーが定義されている場合にのみ機能します。 特定のテストで不必要なブレークポイントは、システムを導くべきではありません(そしてテストを複雑にします)。
  3. コントローラーは、ブレークポイントがどの状態にあるかを知っています-ストリームを保持しますか。
  4. コントローラは、ブレークポイントを強制的にストリームを解放することができます。
  5. 特定のスレッドへのオプションのバインディング。 特定のフローを制御したい、全員を一度にしたい。




 [TestMethod] public void StopStartThreadsTest_exemple1() { var log = new List<string>(); ThreadStart act1 = () => { Breakpoint.Define("empty"); Breakpoint.Define("start1"); log.Add("after start1"); Breakpoint.Define("step act1"); log.Add("after step act1"); Breakpoint.Define("finish1"); }; ThreadStart act2 = () => { Breakpoint.Define("start2"); log.Add("after start2"); Breakpoint.Define("step act2"); log.Add("after step act2"); Breakpoint.Define("finish2"); }; using (var start1 = new BreakCtrl("start1")) using (var step_act1 = new BreakCtrl("step act1")) using (var finish1 = new BreakCtrl("finish1")) using (var start2 = new BreakCtrl("start2")) using (var step_act2 = new BreakCtrl("step act2")) using (var finish2 = new BreakCtrl("finish2")) { var thr1 = new Thread(act1); thr1.Start(); var thr2 = new Thread(act2); thr2.Start(); start1.Wait(thr1); start2.Wait(thr2); start1.Run(thr1); step_act1.Wait(thr1); step_act1.Run(thr1); finish1.Wait(thr1); start2.Run(thr2); step_act2.Wait(thr2); step_act2.Run(thr2); finish2.Wait(thr2); finish1.Run(thr1); finish2.Run(thr2); thr1.Join(); thr2.Join(); } Assert.AreEqual(4, log.Count); Assert.AreEqual("after start1", log[0]); Assert.AreEqual("after step act1", log[1]); Assert.AreEqual("after start2", log[2]); Assert.AreEqual("after step act2", log[3]); }
      
      







本当に不快ですか? しかし、我慢する必要があります-結局、テストなしでは、リファクタリングは不可能です。 あなたは常に最初に自分自身を越えなければなりません...なんとか何とか。 すぐにそれを使用することは不可能であることに気付きました。 私は2番目の10の筆記試験の分野で理解しました。 テストは愛され、複雑です。 しかし...



難しいです。 結局のところ、私は複雑さを解決する以外には何もできません。 少しの努力で、このソリューションが得られました。



 public class ThreadTestManager { public ThreadTestManager(TimeSpan timeout, params Action[] threads){…} public void Run(params BreakMark[] breaks){…} } public class BreakMark { public string Name { get; private set; } public Action ThreadActor { get; private set; } public bool Timeout { get; set; } public BreakMark(string breakName){…} public BreakMark(Action threadActor, string breakName){…} public static implicit operator BreakMark(string breakName){…} }
      
      







使用する場合、前のテストは次のようになります。



 [TestMethod] public void StopStartThreadsTest_exemple2() { var log = new List<string>(); Action act1 = () => { Breakpoint.Define("before start1"); Breakpoint.Define("start1"); log.Add("after start1"); Breakpoint.Define("step act1"); log.Add("after step act1"); Breakpoint.Define("finish1"); }; Action act2 = () => { Breakpoint.Define("before start2"); Breakpoint.Define("start2"); log.Add("after start2"); Breakpoint.Define("step act2"); log.Add("after step act2"); Breakpoint.Define("finish2"); }; new ThreadTestManager(TimeSpan.FromSeconds(1), act1, act2).Run( "before start1", "before start2", "start1", "step act1", "finish1", "start2", "step act2", "finish2"); Assert.AreEqual(4, log.Count); Assert.AreEqual("after start1", log[0]); Assert.AreEqual("after step act1", log[1]); Assert.AreEqual("after start2", log[2]); Assert.AreEqual("after step act2", log[3]); }
      
      







マネージャーのプロパティ:
  1. すべてのデリゲートは、起動時にスレッドで起動されます。
  2. ブレークポイントマーカーは、作業を再開する方法を決定します。 入口ではなく、ブレークポイントからの出口。 おそらくこれは、ブレークポイント抽象化を実装するための単なるコストです。 しかし、プロパティがあり、それについて覚えておく必要がある場合があります。
  3. 対応するブレークポイントマーカーのすべてのコントローラーは、ディスパッチャーの作業全体で定義されます。
  4. ブレークポイントマーカーを使用すると、動作するスレッド(デリゲート)を指定できます。 デフォルトでは、すべてのユーザーで機能します。



     [TestMethod] public void ThreadMarkBreakpointTest_exemple3() { var log = new List<string>(); Action<string> act = name => { Breakpoint.Define("start"); log.Add(name); Breakpoint.Define("finish"); }; Action act0 = () => act("act0"); Action act1 = () => act("act1"); new ThreadTestManager(TimeSpan.FromSeconds(1), act0, act1).Run( new BreakMark(act0, "finish"), new BreakMark(act1, "start"), new BreakMark(act1, "finish")); Assert.AreEqual(2, log.Count); Assert.AreEqual("act0", log[0]); Assert.AreEqual("act1", log[1]); }
          
          



  5. すべての操作を実行する必要がある時間の定義-タイムアウト。 超過すると、すべてのスレッドが大まかに、容赦なく停止します(中止)。
  6. ブレークポイントマーカーに、アクセス不能の兆候を追加できます。ここに到達しない場合、システムはタイムアウトで終了します。 ブレークポイントをトリガーすると、テストが失敗します。 このメカニズムは、ブロックの事実を確認するために使用されます。



     [TestMethod] public void Timeout_exemple4() { var log = new List<string>(); Action act = () => { try { while (true) ; } catch (ThreadAbortException) { log.Add("timeout"); } Breakpoint.Define("don't work"); }; new ThreadTestManager(TimeSpan.FromSeconds(1), act).Run( new BreakMark("don't work") { Timeout = true }); Assert.AreEqual("timeout", log.Single()); }
          
          



  7. フローを停止して続行しない場合は、タイムアウトのあるマーカーの後に適切なブレークポイントマーカーを指定する必要があります。



     [TestMethod] public void CatchThread_exemple5() { var log = new List<string>(); Action act0 = () => { bool a = true; while (a) ; Breakpoint.Define("act0"); }; Action act1 = () => { Breakpoint.Define("act1"); log.Add("after act1"); }; new ThreadTestManager(TimeSpan.FromSeconds(1), act0, act1).Run( new BreakMark("act0") { Timeout = true }, "act1"); Assert.IsFalse(log.Any()); }
          
          







PS: ソリューション



All Articles