6

非常に計算量の多い計算を実行するプログラムを作成する必要があります。プログラムは数日間実行される場合があります。計算は、共有データを必要とせずに、さまざまなスレッドで簡単に分離できます。現在のステータスを通知するGUIまたはWebサービスが必要です。

私の現在の設計では、BOOST::signals2とBOOST::threadを使用しています。コンパイルされ、これまでのところ期待どおりに機能します。スレッドが1回の反復を終了し、新しいデータが利用可能な場合、GUIクラスのスロットに接続されているシグナルを呼び出します。

私の質問:

  • この信号とスレッドの組み合わせは賢明なアイデアですか?私は別のフォーラムで、誰かが「この道を進んで」はいけないと誰かにアドバイスしました。
  • 私が見落とした潜在的な致命的な落とし穴が近くにありますか?
  • GUIクラスを使用してWebインターフェイスやQT、VTKなどのウィンドウを提供するのが「簡単」になるという私の期待は現実的ですか?
  • 私が見落としていた(他のブーストライブラリのような)より賢い代替手段はありますか?

次のコードはでコンパイルされます

g++ -Wall -o main -lboost_thread-mt <filename>.cpp

コードは次のとおりです。

#include <boost/signals2.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <iostream>
#include <iterator>
#include <string>

using std::cout;
using std::cerr;
using std::string;

/**
 * Called when a CalcThread finished a new bunch of data.
 */
boost::signals2::signal<void(string)> signal_new_data;

/**
 * The whole data will be stored here.
 */
class DataCollector
{
    typedef boost::mutex::scoped_lock scoped_lock;
    boost::mutex mutex;

public:
    /**
     * Called by CalcThreads call the to store their data.
     */
    void push(const string &s, const string &caller_name)
    {
        scoped_lock lock(mutex);
        _data.push_back(s);
        signal_new_data(caller_name);
    }

    /**
     * Output everything collected so far to std::out.
     */
    void out()
    {
        typedef std::vector<string>::const_iterator iter;
        for (iter i = _data.begin(); i != _data.end(); ++i)
            cout << " " << *i << "\n";
    }

private:
    std::vector<string> _data;
};

/**
 * Several of those can calculate stuff.
 * No data sharing needed.
 */
struct CalcThread
{
    CalcThread(string name, DataCollector &datcol) :
        _name(name), _datcol(datcol)
    {

    }

    /**
     * Expensive algorithms will be implemented here.
     * @param num_results how many data sets are to be calculated by this thread.
     */
    void operator()(int num_results)
    {
        for (int i = 1; i <= num_results; ++i)
        {
            std::stringstream s;
            s << "[";
            if (i == num_results)
                s << "LAST ";
            s << "DATA " << i << " from thread " << _name << "]";
            _datcol.push(s.str(), _name);
        }
    }

private:
    string _name;
    DataCollector &_datcol;
};

/**
 * Maybe some VTK or QT or both will be used someday.
 */
class GuiClass
{
public:
    GuiClass(DataCollector &datcol) :
        _datcol(datcol)
    {

    }

    /**
     * If the GUI wants to present or at least count the data collected so far.
     * @param caller_name is the name of the thread whose data is new.
     */
    void slot_data_changed(string caller_name) const
    {
        cout << "GuiClass knows: new data from " << caller_name << std::endl;
    }

private:
    DataCollector & _datcol;

};

int main()
{
    DataCollector datcol;

    GuiClass mc(datcol);
    signal_new_data.connect(boost::bind(&GuiClass::slot_data_changed, &mc, _1));

    CalcThread r1("A", datcol), r2("B", datcol), r3("C", datcol), r4("D",
            datcol), r5("E", datcol);

    boost::thread t1(r1, 3);
    boost::thread t2(r2, 1);
    boost::thread t3(r3, 2);
    boost::thread t4(r4, 2);
    boost::thread t5(r5, 3);

    t1.join();
    t2.join();
    t3.join();
    t4.join();
    t5.join();

    datcol.out();

    cout << "\nDone" << std::endl;
    return 0;
}
4

2 に答える 2

13

このシグナルとスレッドの組み合わせは賢明な考えですか? 私は別のフォーラムで、誰かが他の誰かに「この道を行く」べきではないと忠告しました。

響きそうです。他のスレッドへのリンクを提供できますか? 彼らは理由を説明していましたか?

私が見逃した致命的な落とし穴が近くにありますか?

もしそうなら、私もそれらを見ることができません。注意する必要があるのは、通知がスレッドセーフであることです (シグナルのトリガーはスレッド コンテキストを切り替えないためGuiClass::slot_data_changed、他のすべてのスレッドから呼び出される必要があります。

私の GUI クラスを使用して Web インターフェイスや QT、VTK、その他のウィンドウを提供するのは「簡単」であるという私の期待は現実的ですか?

簡単ではありません。これを修正するには、通知でスレッド コンテキストを切り替える必要があります。これが私がすることです:

独自GuiClassのメッセージキューを実装して、抽象基本クラスにします。がGuiClass::slot_data_changedスレッドによって呼び出されると、ミューテックスをロックし、受信した通知のコピーを内部 ( private:) メッセージ キューにポストします。のスレッドでGuiClass、ミューテックスをロックし、キューで通知を探す関数を作成します。この関数は、クライアント コードのスレッド (abstract から特化した具象クラスのスレッド) で実行する必要がありますGuiClass

利点:

  • 基本クラスは、スレッドコンテキストの切り替えをカプセル化して分離し、その特殊化に対して透過的にします。

短所:

  • クライアント コードは、ポーリング メソッドを実行するか、(スレッド処理関数として) 実行できるようにする必要があります。

  • 少し複雑です:)

私の GUI クラスを使用して Web インターフェイスや QT、VTK、その他のウィンドウを提供するのは「簡単」であるという私の期待は現実的ですか?

そうは見えませんが、そう簡単ではありません。スレッドのコンテキスト切り替えに加えて、私が現時点で見逃している他の問題があるかもしれません。

私が見落としていた、より賢い代替手段 (他のブースト ライブラリなど) はありますか?

他のブースト ライブラリではありませんが、スレッドの記述方法が適切ではありません。結合はコード内で順番に行われます。すべてのスレッドに対して1 つだけをjoin使用するには、boost::thread_group を使用します。

それ以外の:

boost::thread t1(r1, 3);
boost::thread t2(r2, 1);
boost::thread t3(r3, 2);
boost::thread t4(r4, 2);
boost::thread t5(r5, 3);

t1.join();
t2.join();
t3.join();
t4.join();
t5.join();

あなたが持っているでしょう:

boost::thread_group processors;
processors.create_thread(r1, 3);
// the other threads here

processors.join_all();

編集:スレッドコンテキストは、実行中の特定のスレッドに固有のすべてのものです(スレッド固有のストレージ、そのスレッドのスタック、そのスレッドのコンテキストでスローされた例外など)。

同じアプリケーション (複数のスレッド) にさまざまなスレッド コンテキストがある場合、スレッド コンテキスト内で作成され、異なるスレッドからアクセスされるリソースへのアクセスを同期する必要があります (ロック プリミティブを使用)。

たとえば、aあるclass A[スレッド tA で実行]bのインスタンスがあり、class B[スレッド tB のコンテキストで実行]のインスタンスがあり、何かbを伝えたいとしますa

「何かを伝えaたい」という部分は、tB のコンテキストで (スレッド B のスタック上で)b呼び出したい、呼び出さa.something()れることを意味します。a.something()

これを変更するには ( a.something()tA のコンテキストで実行するには)、スレッド コンテキストを切り替える必要があります。これは、「実行する」とb伝える代わりに、 「独自のスレッド コンテキストで A::something()` を実行する」と伝えることを意味します。aA::something()ba

従来の実装手順:

  • batB 内からにメッセージを送信します

  • atA 内からのメッセージのポーリング

  • aからのメッセージを見つけると、tAb内で a.something() 自体を実行します。

これは、スレッド コンテキストの切り替えです ( の実行は、A::somethingから直接呼び出された場合と同様に、tB ではなく tA で実行されますb)。

あなたが提供したリンクから、これはすでに によって実装されてboost::asio::io_serviceいるようです。そのため、それを使用する場合は、自分で実装する必要はありません。

于 2010-06-11T11:17:38.137 に答える
7

非常に重要な落とし穴が 1 つあります。

私がsignals2のスレッドセーフを理解している限り、スロットはシグナルスレッドで実行されます。ほとんどの GUI ライブラリ (特に Qt と OpenGL)は、単一のスレッドからすべての描画を行う必要があります。これは一般的には問題ありませんが、少し注意が必要です。次の 2 つのオプションがあります。

1 つ目は、内部で描画を行わないように注意することですGuiClass::slot_data_changed(Qt を使用しているため、QCoreApplication::postEvent を参照してください (申し訳ありませんが、Qt ドキュメントへのリンクを投稿することは許可されていません))。

2 つ目は、スロット呼び出しを保存して GUI スレッドで実行するメッセージ キューを自分で作成することです。GUI クラスはスレッド セーフを気にせずに記述できるため、これはやや面倒ですが、安全でもあります。

于 2010-06-11T12:39:24.147 に答える