制約付きのジョブキューを最適に設計するにはどうすればよいですか?


9

次の状況を考慮してください。

  • 処理する必要のある多数の「ジョブ」を作成し、それらをキューに入れるプログラムがあります。
  • 他のワーカープログラムで次の「ジョブ」を取得して、そのジョブを処理できるようにします。
  • 各ジョブにはカテゴリがあります。
  • カテゴリはいくつあってもかまいません。
  • 同じカテゴリの2つのジョブを別々のワーカーで同時に処理することはできません。
  • ワーカーは一度に1つのジョブを処理できます。

同じカテゴリの複数のジョブが同時に処理される可能性があるため、この状況では従来のキューは機能しませんが、これは許可されていません。

取得するジョブをワーカーに確認させ、そのジョブカテゴリに現在処理中の別のワーカーがあるかどうかを確認し、そうである場合は後で処理するためにジョブをキューに再送信します。これは、この問題を解決する非効率的な方法のようです。この問題を解決できるデータ構造または設計パターンはありますか?

さらに説明が必要な場合は、お知らせください。


トピックによる制限の背後にある理由は何ですか?多分それを変えることができます。
アンディ

@Andy質問に統合する方法がわからないので、ここで返信します。これは実際にはハードウェアの制限です。各カテゴリには、相互作用する必要がある特定のハードウェアリソース(同じポートでの接続)があるため、2つのジョブが同時に同じデバイスと相互作用することはできません。
merp

@merp何か見つけましたか?私は非常によく似たものを探しています。共有/排他ロックやセマフォを宣言するジョブが必要です。排他ロックのみが必要なことを除いて、ケースは似ています。
Guillaume86

回答:


3

この問題には2つの部分があります。

1つ:考えられるカテゴリの不明なリスト。

2つ:同じカテゴリの2つのジョブが同時に処理されるのを防ぐための、ワーカー間のプロセス間通信。

カテゴリの既知のリストがある場合は、カテゴリごとに1つのキューと1つのワーカーを持つことができます。

カテゴリが不明な場合でも、カテゴリごとにキューを設定できますが、カテゴリごとにキューワーカーがある場合は、すべてのキューを監視し、新しいカテゴリが表示されたときに新しいワーカーを起動する必要があります。

これは、仕事を手渡す「マスター」ワーカーで達成できます

すべてのジョブは「マスター」キューに入れられます。

カテゴリワーカーはプライベートキューを作成し、作業に使用できるようにマスターに登録します。

マスターワーカーはジョブを取得し、カテゴリをチェックし、使用可能なワーカーをチェックし、プライベートキューに入れることにより、ジョブの1つに割り当てます。

マスターは、ワーカーに割り当てられたカテゴリを追跡できます。

マスターは次のジョブをピックアップしますが、同じカテゴリであり、ワーカーはまだビジーであるため、ジョブをカテゴリ固有の保留キューに入れます

マスターは次のジョブを取得します。その新しいカテゴリーは別のカテゴリーワーカーに割り当てます。

カテゴリーワーカーはジョブを完了し、仕事に再登録します

マスターは保留キューとマスターキューの次のジョブをチェックし、使用可能なカテゴリワーカーに割り当てます。

カテゴリワーカーがジョブ中にクラッシュした場合、再登録されません。したがって、マスターは、タイムアウトロジックを使用して、別のワーカーにカテゴリを割り当て、割り当てを開始することができます。

また、常に1つのマスターワーカーのみが存在するように注意する必要があります。これは、何らかのマスターキューの排他ロックを必要とします


2

あなたの非効率的な提案の欠点は、カテゴリに2つのジョブがある場合です。今、一人は働いています。そして他の皆は忙しい待機をしています。

これを十分に行うには、ワーカーにキューをスキャンして次の実行可能なタスクをスキャンさせ、見つかった場合はそれ以外のすべてをキューに返します。代わりにすべてを返し、次にスリープします。スリープにランダム性と指数バックオフがある場合、「ビジー待機」はビジーではありません。

より効率的なアプローチのために、仕事をつかむ労働者は、そのカテゴリーから他に何もすることがなくなるまで、そのカテゴリーを行う責任があります。次に、正規労働者に戻ります。しかし、いくつかの微妙な点があります。

それらを表示するには、我々ができると仮定しますtryreleaseロック(両方とも非ブロッキング)と私たちのキュー操作がありaddgetかつis_emptygetブロックし、待機運転さ。

一般的なキューを想定し、次に各カテゴリに対してキューとロックを想定します。

基本的なワーカーフローは次のとおりです。

while obj = get from main queue:
    if try category lock:
        do obj job
        do_whole_category_queue()
    else:
        add obj to category queue
        if try category lock:
            do_whole_category_queue()

どこ

procedure do_whole_category_queue
    while not category queue is_empty:
        obj = get from category queue
        do obj job
    release category lock
    if not is_empty category queue:
        if try category lock:
            do_whole_category_queue()

ここでの注意深いロックハンドシェイクに注意してください。ワーカーはロックをテストし、ロックされている場合はジョブをキューに追加します。ただし、ロックを再度テストして、実際にジョブを実行するのがまだ他の誰かの責任であることを確認する必要があります。キューの操作中にカテゴリワーカーが終了した場合に備えて。

(これは、人々がしばしば失敗する、ロックの詳細の一種です。エラーは確実に再現することは不可能ですが、本番環境では不規則かつ不可解なほど失敗します...)


任意の数のカテゴリにできる場合は、スケーリングするのが難しくなりますl。全体的に分散環境にある場合。さらに、異なるランタイムの2つのワーカーが同じジョブを消費することを回避するために、他のすべてのランタイムのカテゴリキューをチェックすることはできません。私は、マスターキュー/ワーカーをサービスディスパッチタスクとして、またはMasterWork +水平キャッシュを備えた分散マスターキューとして使用します。最初のアプローチでも(サービスとして)キャッシュを使用します。次に、このキャッシュのバランスを取り、同期化する方法にスケーラビリティが残ります。タイムアウトをリキューに設定した場合、ブロッキングキューは問題になりません
Laiv

1
キューはかなり安価です。短いままである可​​能性が高いものはさらに安いです。たとえば10万のカテゴリを下回っている場合、これは問題にはなりません。コメントのカテゴリは、特定のポートで開いている接続であるハードウェアリソースにマップされるため、そのような制限を超えることはほとんどありません。
btilly
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.