5

私は以下で構成されるシステムに取り組んでいます。

  1. データベース
  2. このデータベースに行をポンピングするスレッドの束
  3. これらの行で動作するスレッドの束

重要な点は、複数のワーカーが同時に行を操作してはならないということです。

もちろん、ここにはたくさんの戦略があります...データベースレベルで物事をロックしたり、ミューテックスを使用したりできます。

しかし、これをどのように実装するかに関係なく、システムをテストして、正しく実行したことを確認できる必要があります。

これをテストする適切な方法は何ですか?

私が行っているのは、何百ものスレッドを実行し、偶発的なオーバーラップを継続的にチェックすることだけです。問題は、当たりか外れかです。確率です。500 スレッドを 1 時間実行したとしても、別のスレッドとオーバーラップするスレッドが存在する可能性はほとんどありません。

また、オーバーラップを適切にチェックするにはどうすればよいですか? 「オーバーラップ チェッカー」自体には有限の解像度があり、発生している実際のオーバーラップを見逃す可能性があります...

並行性が複雑なトピックであることは承知していますが、そのようなシステムを長時間実行して指を交差させるだけでなく、そのようなシステムをテストするためのベストプラクティスまたは推奨される方法が確かにあるはずです...

4

10 に答える 10

4

データベースに依存する必要があります。指を交差させる必要はありません。これは、トランザクション/ ACID (wikipedia)をサポートするための実際のデータベースのコア機能です。何か不足していますか?同じ行で機能するために「スレッドが重複する可能性がある」とはどういう意味ですか?

于 2012-09-22T11:55:48.560 に答える
0

そのようなことをするために私が見つけた最善の方法は、大量のスレッドを開始してから、スレッドをランダムに中断および再開することによってランダムなジッターを導入することでした。

これにより、多くの興味深いスレッドスケジューリングが可能になります。これはモンテカルロアルゴリズムであり、検索スペースを徐々にカバーします。

スレッドをランダムにスケジュールできるわけではありません。エラーが存在しないことを表明する必要があります。あなたの場合の私の最善のアイデア:新しい列を追加しますWorkerCount int not null。ゼロに初期化します。ワーカーが実行されたら、インクリメントします(1に設定しないでください-インクリメントします)。チェック制約をそこに置いてチェックしますWorkerCount IN (0, 1)。ワーカーが終了したら、カウントをデクリメントします。

これは最初のオーバーラップで壊れます。

于 2012-09-12T21:26:15.280 に答える
0

私が前に言った他の人たちのように、唯一の適切な方法はあなたのために働くものです. 善悪はなく、善悪しかありません。そうは言っても:

あなたの目標は次のとおりです。

重要な点は、複数のワーカーが同時に行を操作してはならないということです。

したがって、何千ものスレッドが開いていても、変更を実行できるスレッドは 1 つだけです。スレッドは動的に作成されるため、それらを追跡するのは難しく、リソースと時間の無駄になります。

ただし、データベースは一意であるため、スレッドを制御する代わりに、データベースをボトルネックにして、特定の行ごとに 1 つのスレッドのみを許可することができます。これを行うには、列ごとに新しいチェッカーを挿入する必要があります。列にThreadCheckという名前を付けましょう。この例ではテキストにします。

1 アイデア 1 - スレッド カウンター

列の背後にある考え方は、データベース内のある行を操作する各スレッドが Thread[Thread ID] として ThreadCheck に保存されるというものです。行に対するスレッドの操作が完了すると、ThreadCheck は null または特定の値として保存されます。この例では「空」という名前を付けます。そのため、スレッドの処理が終了し、値が空に変わります。

2 アイデア 2 - キュー

次に実装するのはqueueです。キューのロジックは FIFO (先入れ先出し) に基づいており、銀行の待機リストをシミュレートできます。銀行では、複数の人が複数のクライアントにサービスを提供しようとしています。ただし、アプリの場合、この銀行には、多数のクライアント (スレッド) にサービスを提供しようとしている人物が 1 人しかいません。キューが構築されると、新しいスレッドごとに待機チケットが与えられます。これは、スレッドが待機リストにある場所をシミュレートする別の単純なカウンターです。

スレッドが行を操作したい場合、その行が空で、そのスレッドにカウンター + 1 の値の待機チケットがある場合、そのスレッドを操作できます。カウンターが 1 の場合、待機中のスレッドはカウンター 2 を持ち、次のスレッドは X までカウンター 3 を持ちます。

ラインを前に進めるには、警備員やマネージャーなど、人々に「次へ!!!」と伝える優れた人物が必要です。スレッド待機リストでは、この優れた機能は定期的なループであり、現在のスレッドが作業を終了した場合にのみ行を前方に移動します。これにより、時間とリソースの両方が節約され、定期的なチェックではなくループ イベント ベースになります。

動的な counter++ と counter-- を使用して合計キューを拡張し、スレッドが終了するたびに待機中のチケットに新しい値を与えることができます。または、一度にアクティブな 1234567890 スレッドのようなキュー制限を適用します。キューが移動するまで、新しいスレッドは拒否されます。

3 アイデア 3 - まとめ

スレッドが作成され、待機チケットが与えられます。スレッドが終了すると、サービス カウンターは行を 1 つ上に移動します。最終スレまで全部。スレッド チェックとキューをプッシュするセキュリティ機能を使用すると、すべてが同時に動作しようとするスレッドの群れではなく、ボトルネックが発生します。

私が精緻化しようとしたロジックに問題がある場合、または一部の部分が理解できない場合は、コメントしてください。

また、これは自分で解決しようとするため、独自のコードを作成するのに役立つコードと、最初から何かを作成しようとする手間を省くコードを意図的に含めていません。

于 2012-09-24T17:27:28.013 に答える
0

これをテストする適切な方法は何ですか?

ここには簡単な答えはありません。考慮すべきことの 1 つは、オーバーラップをキャッチするワーカー スレッドとデータベース レイヤーの間に挿入できるデータベース プロキシを作成することです。クライアント操作の行 ID をいくつかの並行マップに記録し、データベース RPC 呼び出しが終了したときにそれを削除できます。

Java を疑似コードとして使用します。

 try {
     // add it to some atomic concurrent hash-map
     if (workingRowIdMap.putIfAbsent(rowId, null) != null) {
         // scream and shout and log a concurrency failure
     }
     // do the _real_ database stuff here
 } finally {
     workingRowIdMap.remove(rowId);
 }

SQL を扱っている場合、問題のデータの行 ID を抽出するのは難しいかもしれません。扱っている SQL が複雑な場合は不可能かもしれません。スキーマに関する詳細がなければ、知ることは困難です。

また、プロキシをできるだけ軽量にする必要があります。そうしないと、ロックを追加したり、メモリ バリアを追加したりすると、問題が隠れてしまう可能性があります。


もう 1 つのアイデアは、データベース ツールを使用してバイナリ変更ログを調べ、指示に重複がないかどうかを確認することです。MySQL には、バイナリログを調査できるツールがいくつかあることを知っています。ただし、データの更新が重複するかどうかを判断できるようにツールをカスタマイズするには、多くの作業が必要になる場合があります。


また、オーバーラップを適切にチェックするにはどうすればよいですか? 「オーバーラップ チェッカー」自体には有限の解像度があり、発生している実際のオーバーラップを見逃す可能性があります...

同意した。バグのテストは、正確な科学ではありません。特に、並行性の高いソフトウェアの場合はそうです。アイデアは、それに全力を尽くすことです。私は常に、実際のサービス トラフィックをシミュレートするようにしています。これは、多くの場合、ソフトウェアが本番環境で見られる条件を再現するための最良の方法です。本番ログを再生すると、問題の検出に役立つ場合があります。

テストとは別に、ソフトウェアの同時実行性の高い部分についてグループ コード レビュー セッションを行うことも保証される場合があります。そのコードを適切に分離して、適切な try / finally ブロックなどでロックが簡単になるようにすることも、適切な投資になります。

于 2012-09-12T21:06:36.627 に答える
0

あなたの状況を完全には理解していないかもしれませんが、以下は、C# で複数のスレッドを使用してデータベースに数千のレコードを保存するために使用しているものです。

デッドロック状態にならずに複数のレコードを同時にデータベースに保存するには、SQLBulkCopy (ADO.Net) または一括挿入 (SQL Server) ユーティリティを使用できます。

それらは同時実行性を維持し、デッドロック状態になることはありません。

ロギングのために、シングルトン クラスを作成し、そのオブジェクトをすべてのスレッドに渡しました。コードに戻ると、ConcurrentList と Lock(object) を使用してすべてのログを保存し、5 秒ごとにそれらのすべてのレコードをデータベースに保存してクリーニングしています。再び SQLBulkCopy コマンドを使用して、リスト。

さらに情報が必要な場合はお知らせください...

于 2012-09-18T08:33:36.157 に答える
0

質問で言ったように、パフォーマンスよりもテスト容易性に焦点を当てる必要があります。

生産者/消費者モデルを提案します。データベース (新しい行) に書き込みたいスレッドをいくつでも持つことができ、データベース サーバーに同時実行性を処理させることができます。これはシステムの最初の部分であり、多くのスレッドが行をテーブルにポンピングします。

各行を 1 回だけ処理するには、新しい行をロードして Queue に送り込む単一のスレッドを使用することをお勧めします。次に、キューを処理したい数のスレッドを持つことができます。処理が完了すると、データベースの行を更新したり、別のスレッドがバッチで更新要求を収集して処理する出力キューに書き込んだりできます。

テーブルに PROCESSING_STATUS 列があり、新しい行には常に PROCESSING_STATUS = 0 があるとします。したがって、スレッドはこのテーブルに新しい行を自由に追加できます。別のスレッドは、PROCESSING_STATUS = 0 のすべての行を選択して、このテーブルを (事前定義された間隔/イベントで、または単純にポーリングして) 継続的にクエリします。次に、各行がキューに追加されます。ロードしたら、PROCESSING_STATUS を 1 に更新できます。再度クエリを実行する前にこれを完了する必要があります。これは、同じ行を 2 回ロードしないようにするために重要です。

実際のワーカー スレッドはこのキューを消費します。同時キューまたは同様の構造を使用して、多くのコンシューマーを処理できると仮定します。キュー アルゴリズムは、1 つのスレッドだけが同じ要素を取得できることを保証する必要があります。この種の Queue は、Python、C#、または Java の標準ライブラリで簡単に見つけることができます。次に、実際のトレッドがこの行を処理し、出力キューに書き戻します。

行の書き戻しを担当するスレッドは、作業スレッドが生成したデータと PROCESSING_STATUS 列を更新し、たとえば 2 に設定します。この更新は、読み取り後に変更されていないことを確認するために、行のすべての既知のキーと値を使用して実行する必要があります。書き込みスレッドは、更新クエリで影響を受ける行の値も確認し、処理後に行が削除または変更されていないかどうかを確認する必要があります。

テスト容易性に関しては、PROCESSING_STATUS 列を調べて、未処理の行があるかどうかを確認できます。PROCESSING_STATUS=0 の場合 - この行はロードされていません。1 の場合、ロードされましたが、処理/書き戻しはされていません。2 は、処理されたことを意味します。各行の処理が正しく行われたかどうかを確認する必要がありますが、これは標準的なテストです。

複数のスレッドが同じ行にアクセスしようとしたかどうか、または更新ステートメントで影響を受ける行をチェックして、最初に読み取られた後に行が変更されたかどうかを確認できます。更新によって影響を受ける行がない場合は、すでに処理または変更されていることを意味します。

したがって、このシナリオでのテスト容易性の鍵は、スレッドの同期にキューを使用し、データベースへの更新をチェックすることです。キューと処理スレッドのカウンターを使用して、ロードされた行数 = 処理された行数 = 書き込まれた行数であるかどうかを確認することもできます。

データベースからデータをロードする多くのスレッドが必要な場合は、PROCESSING_STATUS 列の使用も拡張できます。未処理の (新しい) 行が PROCESSING_STATUS = 0 で追加されると想像してください。次に、読み取りスレッドのセット (それぞれが正で 0 とは異なる一意の番号を持つ) は、限定された select ステートメントと更新を結合します。何かのようなもの:

update TABLE_X set PROCESSING_STATUS = MY_UNIQUE_THREAD_ID
where key in (select key from TABLE_X where PROCESSING_STATUS = 0 LIMIT 5)
      and PROCESSING_STATUS = 0

影響を受ける行がゼロでない場合、このスレッドにはロードする行がいくつかあります。次のステップは、PROCESSING_STATUS = MY_UNIQUE_THREAD_ID であるすべての行をロードすることです。その後、同じアルゴリズムを再度使用できます。行が処理されると、その PROCESSING_STATUS が MY_UNIQUE_THREAD_ID の負の値で更新されます。このようにデータベースを使用して同時実行を処理しますが、最高のパフォーマンスが得られるわけではありません。少なくとも、すべての行を 1 回だけ処理するという元の問題は解決されます。

データベース サーバーに負荷をかけずに行を 1 回だけロードする別の方法は、キーに対してモジュロ演算を使用することです (キーがシリアル キーの場合)。select ステートメントのキー (k % n_readers) にモジュロを使用します。ロードするには:

SELECT * from TABLE_X WHERE (key % N) == MY_UNIQUE_THREAD_ID
于 2012-09-20T11:34:48.727 に答える
0

テストする必要がないので、作業の一部を抽出してrow_idでチェーンすることをお勧めします。

.NET では、次のようなことを行います。

private var rowWorkers = new Dictionary<int,Task>();

public void ScheduleWorkOnRow(int id) 
{
  // starting empty worker to be able to continue on it
  if(rowWorkers[id] == null) rowWorkers.Add(id, Task.Run(() => { });
  // scheduling continuation
  rowWorkers[id].ContinueWith(WorkOnRow, id);
}

private void WorkOnRow(Task task, object id)
{
  //your code
}

このスニペットは理想とはかけ離れていますが、要点は理解できると思います。

于 2012-09-24T12:53:54.140 に答える
0

オーバーラップを実際にテストするには;

  • オーバーラップすることが保証されているテストセットを作成します (例: 同一の行を 1 つだけ入力します)。
  • ロック メカニズムが機能していることを確認します (たとえば、すべての行で作業の開始と停止をログに記録します)。

パフォーマンスをテストするには、実稼働のような方法でデータを生成するテストセットを作成する必要があります。同等のハードウェアを使用して...

行のロックに関しては、すべてが1つのアプリケーションにある場合、作業中の行のIDまたはそれらの行に沿ったものでConcurrentDictionaryを作成すると思います。または、行がキューイング/デキューによって処理されている ConcurrentQueues を備えたシステムを使用します。

于 2012-09-21T08:40:09.127 に答える
0

Heisenbug をトリガーすることを期待して多数のスレッドを起動するのではなく、Chessのようなツールを検討することをお勧めします。私はそれをスピンしたことがないことを認めなければなりませんが、発生するすべてのインターリービングを積極的に調査することを意図しているため、問題に適しているように見えます.

研究者によると、CHESS は Microsoft 内の多くのコード ベースのテスト フレームワークに統合されており、テスターに​​よって日常的に使用されています。

プロジェクトはもともと win32 ソリューションとして開発されましたが、.NETに移植されていることに注意してください。そのページからの codeproject リンクは無効になっていますが、クイック検索でコードがまだ利用可能であることがわかります

于 2012-09-24T19:34:44.390 に答える
0

私はあなたの質問を分割しています。ここで2つのことを混同しているようです。1) 複数のスレッドが同じ行で動作するのを避けるには? 2) アプリケーションをテストして、これが発生していないことを確認する方法は?

あなたが言及していないことの1つは、データベースへの書き込みを行っているスレッドの順序を処理する方法です。ユーザー提供のデータを扱う場合、変更が適用される順序が非常に重要になる場合があります。念のため、これをそこに投げてください。

回避方法:

これに使用しているデータベースは言いません。ハイエンドの商用のものの中には、行ロックや、非常に興味深い他の多くの機能を備えているものがあります。DBA に相談して、解決できないかどうかを確認してください。これを制約する適切な場所がデータベース内にあることに完全に同意します。確実に100%捕まえられるのはここだけです。

とは言っても、絶対に正しくする必要がある場合は、マルチパート ソリューションを使用する必要があります。そうすれば、1 つのことが失敗しても、他の人がそれを補うことができるかもしれません。したがって、ここに記載されている他の対策をいくつか追加してください。

テスト方法:

重複するデータの既知の行を含むデータセットを作成してから、しかめっ面のチェス ツールのようなものを試してください。しかし、コードがそれらをどのように処理しているかを確認し、適切に処理しているかどうかを確認するには、問題を引き起こすことがわかっているデータが必要です。何かが定着することを期待して、ランダムなデータを投げ続けないでください。たとえば、おそらく同じ主キーを指定したために、すべて同じ行にアクセスしようとしている 500 のスレッドを起動するとどうなるでしょうか?

于 2012-09-24T19:55:04.363 に答える