Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyでの非同期スレッドのテスト

スレッドと非同期環境は、最初は少し注意が必要です。相互作用を整理するための優れたメンタルモデルがないと、トラブルに巻き込まれ、予期しない結果に終わる可能性があります。その上、非同期コードのテストは、適切なツールやテストパターンがないと難しい場合があります。

スレッドを人として、共有オブジェクトを所有可能な「もの」として考えると、マルチスレッドシステムの動作を整理するのに役立ちます。このエピソードでは、非同期Rubyコードのテストについてすべてを学ぶための例を紹介します。

RailsやRack、またはWebブラウザーのフロントエンドとして非同期環境にあるアプリケーションを使用している場合。ラック#call 常に非同期で呼び出されます。したがって、知っているかどうかに関係なく、すでにマルチスレッドコンポーネントを使用している可能性があります。

テスト:トリガー、収集、チェック

非同期コールバックAPIのテストは、3つのステップのパターンに従うことで同期させることができます。 トリガー収集 、およびチェック 。各スレッドが個別の個人であり、オブジェクトが、一度に1人の個人だけが所有できるものであるかどうかを考えてください。

バットマンと彼の7つの異なるスーツの例を使用します。それは実際的な例であり、あなたが不足して街を救おうとしているときに、すべてのスーツがアルフレッドと一緒に洗われるかどうかを知ることの重要性を理解できるからです。

例:バットケイブでの洗濯日

例は、バットマンのスーツを洗うアルフレッドです。 SuitWashSchedulerは、各洗浄イベントのコールバックを呼び出すスケジューラーです。スケジューラーは、開始から1秒後に開始して、1秒間隔で7つのコールバックを作成します。トリガーは、SuitWashSchedulerの作成です。 。

class SuitWashScheduler
  def initialize(cnt)
    Thread.new {
      cnt.times {
        sleep(1.0)
        yield
      }
    }
  end
end

収集

結果の収集は、競合状態を回避するためにスレッドセーフである必要があります。複数のスレッドで共有されるオブジェクトはすべて保護する必要があります。保護は、オブジェクトの所有者を追跡する方法です。所有者のみがオブジェクトを変更または表示できます。スーツは、バットマンが戦闘で使用するか、アルフレッドが洗われる場合にのみ使用できます。

友好的であり続けるために(比喩のバットマンまたはアルフレッドの)スレッドは短時間だけ所有権を取得し、その後所有権を放棄します。 Mutex 通常、所有者を追跡するために使用されます。 SuitwashScheduler カウンターがインクリメントされると、コールバックは結果カウンターを所有します。 SuitWashSchedulerで実行されるコールバック スレッドは、カウンターがターゲットにヒットしたときにすべての結果が受信されたことを通知します。

例を書くことは、いくつかのグローバルを設定することから始まります。実際のアプリケーションでは、グローバルはクラスまたはオブジェクトの属性に置き換えられます。

$main_thread = Thread.current
$mu = Mutex.new
$count = 0
$target = 7

管理者と所有者

$main_thread および$mu $targetがスレッドを管理し、テストの完了を待機するために使用されます および$count テスト結果を追跡します。これは簡単なテストであるため、結果の収集と確認は簡単である必要があることを忘れないでください。

テストは、SuitWashSchedulerの新しいインスタンスを作成することから始まります。 、イニシャライザに$targetを与えます 反復回数。この場合、洗濯が必要な7つのスーツ。提供されたブロックはSuitWashSchedulerで実行されます。 スレッド。反復ごとに$count インクリメントされて印刷されます。

将来的には、メインのテストスレッドが$countをチェックすることになります。 また、これは$countの所有権が必要になることを意味します 同様に、$countの所有権を取得する手段 が必要です。 $mu Mutex インスタンスは所有権トークンです。 SuitWashScheduler.newに渡されたブロック内 $mu.synchronizeを呼び出します ブロックは、$countを設定するのに十分な時間所有権を取ります 結果を確認します。結果の詳細については、すぐに確認してください。

SuitWashScheduler.new($target) {
  $mu.synchronize {
    $count += 1
    puts $count
    $main_thread.wakeup if $target <= $count
  }
}

チェック:すべての訴訟は完了しましたか?

メインスレッドに戻り、テストが終了するのを待つ必要があります。バットマンは、7つのスーツがすべて完了するまで待つ必要があります。チェックする条件は2つあります。テストは$countを更新します 予想通り、またはバットマンはテストが終了してタイムアウトするのを待って退屈します。 $countを確認する前に $targetに到達したかどうかを確認します 、$countの所有権 が必要です。 SuitWashSchedulerのブロックと同じように $mu.synchronizeへの呼び出し 使用されます。

しかし、それは正しくありません。メインスレッドをロックすると、SuitWashSchedulerはどのようになりますか。 スレッドは$countを変更します ?幸いなことに、これを処理する巧妙なトリックがあります。 Mutex クラスには#sleepがあります 所有権を放棄し、タイムアウトするか、ウェイクアップされるまで待機するメソッド。タイムアウトまたは#wakeupのいずれかで起こされたら メインスレッドの呼び出し$mu 続行する前に、再び所有権を取得しようとします。所有権が達成されると、結果を確認し、テストの合格または不合格の状態を判断できます。

$mu.synchronize {
  $mu.sleep($target + 1)
  if $target != $count
    puts 'FAILED'
  else
    puts 'Passed! All suits are washed and clean'
  end
}

これをさらに深く知りたい場合は、複数のスケジューラーを作成して、Mutexが$countをどのように維持するかを確認することで、例をもう少し面白くすることができます。 衝突からの変化。まるでバットマンがスーツをアルフレッドに送って洗うように、そして他のスーツをドライクリーニング店に送るかのように。必ずロジックを変更して、$targetを確認してください。 チェックは、予想されるすべての歩留まりの合計です。

まとめ

スレッドと非同期環境での作業は、適切なメンタルモデルで簡単になります。この投稿では、スレッドのメタファーとして人を使用し、共有オブジェクトのメタファーとして物理オブジェクト(スーツ)を使用しました。これは、一度に1人のスレッドまたは人だけが所有できます。 。このように抽象化することで、理解と記憶が容易になると考えています。

の例で非同期のメカニズムを思い出していただければ幸いですが、すべてのスーツを洗っている間にバットマンが不足しているというイメージが長続きしないことを願っています。

P.S.ブログのバットマンの比喩がすべて終わったら、私たちに知らせてください。


  1. Ruby2.6の9つの新機能

    Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,

  2. Rubyスレッドの使用方法:わかりやすいチュートリアル

    Rubyのスレッドとは何ですか? スレッドにより、Rubyプログラムは同時に複数のことを実行できます。 次のようなもの : 複数のファイルを読み取る 複数のウェブリクエストの処理 複数のAPI接続を確立する スレッドを使用した結果、マルチスレッドのRubyプログラムが作成され、作業をより迅速に行うことができます。 しかし、1つの警告… Rubyアプリケーションを実行するデフォルトの方法であるMRI(MatzのRuby Interpreter)では、 i/oバウンドアプリケーションを実行する場合にのみスレッドの恩恵を受けます。 。 この制限は、 GIL(グローバルインタープリター