21

特定のタスクを実行するためにスレッドを生成し、スレッドセーフキューを使用してそれらと通信したいと思います。また、待っている間、さまざまなファイル記述子に対してIOを実行したいと思います。

これを達成するための推奨される方法は何ですか?キューが要素なしから一部の要素に移動したときに、スレッド間パイプを作成して書き込む必要がありますか?もっと良い方法はありませんか?

また、スレッド間パイプを作成する必要がある場合、共有キューを実装するライブラリを増やして、共有キューとスレッド間パイプを単一のエンティティとして作成できないのはなぜですか?

私がこれをやりたいという事実は、根本的な設計上の欠陥を意味しますか?

私はこれをC++とPythonの両方について尋ねています。そして、私はクロスプラットフォームソリューションに少し興味がありますが、主にLinuxに興味があります。

より具体的な例については...

ファイルシステムツリー内のものを検索するコードがいくつかあります。私はいくつかの通信チャネルをソケットを通して外の世界に開いて​​います。ファイルシステムツリー内のものを検索する必要がある(またはない)リクエストが到着します。

1つ以上のスレッドでファイルシステムツリー内のものを検索するコードを分離します。ツリーを検索する必要が生じたリクエストを受け取り、それらをサーチャースレッドによって実行されることのスレッドセーフキューに入れたいと思います。結果は、完了した検索のキューに入れられます。

検索が行われている間、すべての非検索要求に迅速に対応できるようにしたいと思います。タイムリーに検索結果に対応できるようにしたいと思います。

着信要求を処理することは、通常、を使用するある種のイベント駆動型アーキテクチャを意味しますepoll。ディスク検索要求のキューと結果の戻りキューは、ミューテックスまたはセマフォを使用してスレッドセーフを実装するスレッドセーフキューを意味します。

空のキューで待機する標準的な方法は、条件変数を使用することです。しかし、待っている間に他のリクエストを処理する必要がある場合、それは機能しません。結局、結果キューを常にポーリングし(そして、平均してポーリング間隔の半分だけ結果を遅らせる)、リクエストをブロックし、サービスを提供しません。

4

8 に答える 8

11

イベント ドリブン アーキテクチャを使用する場合は常に、イベントの完了を報告する単一のメカニズムが必要です。Linux では、ファイルを使用している場合、select または poll ファミリから何かを使用する必要があります。つまり、パイプを使用して、ファイルに関連しないすべてのイベントを開始する必要があります。

編集: Linux にはeventfdtimerfdがあります。これらをリストに追加して、別のスレッドからトリガーされたとき、またはタイマー イベントでトリガーされepollたときに、それぞれを中断するために使用できます。epoll_wait

別のオプションがあり、それはシグナルです。fcntlファイル記述子がアクティブになったときにシグナルが発行されるように、ファイル記述子を変更することができます。シグナル ハンドラーは、選択した任意のタイプのキューにファイル準備完了メッセージをプッシュできます。これは、単純なセマフォまたはミューテックス/condvar 駆動型キューの場合があります。select/を使用pollしなくなったため、パイプを使用してファイルベースのメッセージをキューに入れる必要がなくなりました。

健康に関する警告: 私はこれを試したことはありません。なぜうまくいかないのかわかりませんが、このsignalアプローチのパフォーマンスへの影響はよくわかりません。

編集:シグナルハンドラでミューテックスを操作することは、おそらく非常に悪い考えです。

于 2011-04-02T18:43:30.523 に答える
5

あなたが言及したこと、pipe()およびlibevent(epollをラップする)を使用して、この正確な問題を解決しました。ワーカー スレッドは、出力キューが空から非空になると、パイプ FD に 1 バイトを書き込みます。これにより、メインの IO スレッドが起動し、ワーカー スレッドの出力を取得できます。これはうまく機能し、実際にはコーディングが非常に簡単です。

于 2011-04-02T18:20:16.353 に答える
4

あなたは Linux タグを持っているので、これを捨てます: POSIX メッセージ キューはこれをすべて行います。

スレッドセーフな同期が組み込まれています。キューの読み取り時にワーカー スレッドをブロックすることができます。別の方法として、MQ は mq_notify() を使用して、新しいアイテムがキューに入れられたときに新しいスレッドを生成する (または既存のスレッドにシグナルを送る) ことができます。また、select() を使用するように見えるので、MQ の識別子 (mqd_t) を select でファイル記述子として使用できます。

于 2011-04-02T18:13:05.543 に答える
3

このオプションについてはまだ誰も言及していないようです:

select/ poll/etcを実行しないでください。あなたの「メインスレッド」で。I/O 操作が完了すると、I/O を実行し、通知をスレッドセーフ キュー (他のスレッドがメイン スレッドとの通信に使用する同じキュー) にプッシュする専用のセカンダリ スレッドを開始します。

次に、メイン スレッドは通知キューで待機するだけです。

于 2016-04-26T04:02:56.527 に答える
3

私の意見では、Duck's と twk's は実際には doron's (OP によって選択されたもの) よりも優れた回答です。doron は、シグナル ハンドラのコンテキスト内からメッセージ キューに書き込むことを提案し、メッセージ キューは「任意のタイプのキュー」にできると述べています。多くの C ライブラリ/システム コールはシグナル ハンドラ内から安全に呼び出すことができないため、これに対して強く警告します ( async-signal-safeを参照)。

特に、ミューテックスで保護されたキューを選択した場合は、シグナル ハンドラからアクセスしないでください。次のシナリオを考えてみましょう: コンシューマー スレッドがキューをロックして読み取ります。その直後に、カーネルはシグナルを配信して、ファイル記述子にデータが含まれていることを通知します。ハンドラーが消費者スレッドで実行されることを通知し、キューに何かを入れようとします。これを行うには、まずロックを取得する必要があります。しかし、すでにロックを保持しているため、デッドロック状態になります。

select/poll は、私の経験では、UNIX/Linux のイベント駆動型プログラムに対する唯一の実行可能なソリューションです。マルチスレッド化されたプログラム内でより良い方法があればいいのにと思いますが、消費者スレッドを「目覚めさせる」ための何らかのメカニズムが必要です。システム コールを必要としないメソッドをまだ見つけていません (select などのブロッキング コール中にコンシューマ スレッドがカーネル内の待機キューにあるため)。

編集: select/poll を使用するときにシグナルを処理するための Linux 固有の方法について言及するのを忘れていました: signalfd(2)。選択/ポーリングできるファイル記述子を取得し、シグナルハンドラーのコンテキストではなく、通常どおりコードを処理します。

于 2012-02-26T15:19:48.650 に答える
1

C++11 には std::mutex と std::condition_variable があります。この 2 つを使用して、特定の条件が満たされたときに 1 つのスレッドが別のスレッドにシグナルを送ることができます。これらのプリミティブからソリューションを構築する必要があるように思えます。環境がこれらの C++11 ライブラリ機能をまだサポートしていない場合は、boost で非常によく似た機能を見つけることができます。申し訳ありませんが、python について多くを語ることはできません。

于 2011-04-02T18:06:48.680 に答える
0

やりたいことを達成する 1 つの方法は、オブザーバー パターンを実装することです。

メインスレッドを生成されたすべてのスレッドにオブザーバーとして登録し、本来の処理が完了したときに通知するようにします (または実行中に必要な情報で更新します)。

基本的に、イベント駆動型モデルへのアプローチを変更したいと考えています。

于 2011-04-02T18:05:17.727 に答える