Je dois écrire un programme qui effectue des calculs très intensifs en calcul. Le programme peut fonctionner pendant plusieurs jours. Le calcul peut être facilement séparé dans différents threads sans avoir besoin de données partagées. Je souhaite une interface graphique ou un service Web qui m'informe de l'état actuel.Signaux et threads - décision de conception bonne ou mauvaise?
Ma conception actuelle utilise BOOST :: signals2 et BOOST :: thread. Il compile et jusqu'ici fonctionne comme prévu. Si un thread a terminé une itération et que de nouvelles données sont disponibles, il appelle un signal qui est connecté à un slot dans la classe GUI.
Ma question (s):
- est cette combinaison de signaux et les fils une bonne idée? Dans un autre forum, quelqu'un a conseillé à quelqu'un de ne pas "suivre cette route".
- Existe-t-il d'éventuels pièges mortels à proximité que je n'ai pas pu voir?
- Est-ce que mes attentes sont réalistes qu'il sera "facile" d'utiliser ma classe GUI pour fournir une interface web ou un QT, un VTK ou une fenêtre quelconque?
- Existe-t-il une alternative plus intelligente (comme d'autres bibliothèques de boost) que j'ai oublié?
code suivant compile avec le code
g++ -Wall -o main -lboost_thread-mt <filename>.cpp
suit:
#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;
}
Alors que cet exemple particulier est bien, vous devriez faire attention - comme vous ne protégez pas votre vecteur avec le mutex dans la fonction 'out' – nos