Je voudrais obtenir quelques commentaires concernant la classe IService listée ci-dessous. D'après ce que je sais, ce type de classe est lié au modèle "objet-actif". Veuillez excuser/corriger si j'utilise une terminologie incorrecte de manière incorrecte. Fondamentalement, l'idée est que les classes utilisant cette classe d'objets actifs doivent fournir une méthode start et une méthode stop qui contrôlent une boucle d'événement. Cette boucle d'événement peut être implémentée avec une boucle while ou avec asio boostquestions relatives à la conception d'objet actif liées au threading (C++ boost)
Cette classe est chargée de démarrer un nouveau thread de manière non bloquante afin que les événements puissent être traités dans/par le nouveau thread. Il doit également gérer tout le code lié au nettoyage. J'ai d'abord essayé une approche OO dans laquelle les sous-classes étaient responsables des méthodes de contrôle de la boucle d'événements mais le nettoyage était désordonné: dans le destructeur appelant la méthode stop, un appel de fonction virtuelle pure était généré dans les cas où la classe appelante méthode d'arrêt. La solution templated semble être beaucoup plus propre:
template <typename T>
class IService : private boost::noncopyable
{
typedef boost::shared_ptr<boost::thread> thread_ptr;
public:
IService()
{
}
~IService()
{
/// try stop the service in case it's running
stop();
}
void start()
{
boost::mutex::scoped_lock lock(m_threadMutex);
if (m_pServiceThread && m_pServiceThread->joinable())
{
// already running
return;
}
m_pServiceThread = thread_ptr(new boost::thread(boost::bind(&IService::main, this)));
// need to wait for thread to start: else if destructor is called before thread has started
// Wait for condition to be signaled and then
// try timed wait since the application could deadlock if the thread never starts?
//if (m_startCondition.timed_wait(m_threadMutex, boost::posix_time::milliseconds(getServiceTimeoutMs())))
//{
//}
m_startCondition.wait(m_threadMutex);
// notify main to continue: it's blocked on the same condition var
m_startCondition.notify_one();
}
void stop()
{
// trigger the stopping of the event loop
m_serviceObject.stop();
if (m_pServiceThread)
{
if (m_pServiceThread->joinable())
{
m_pServiceThread->join();
}
// the service is stopped so we can reset the thread
m_pServiceThread.reset();
}
}
private:
/// entry point of thread
void main()
{
boost::mutex::scoped_lock lock(m_threadMutex);
// notify main thread that it can continue
m_startCondition.notify_one();
// Try Dummy wait to allow 1st thread to resume???
m_startCondition.wait(m_threadMutex);
// call template implementation of event loop
m_serviceObject.start();
}
/// Service thread
thread_ptr m_pServiceThread;
/// Thread mutex
mutable boost::mutex m_threadMutex;
/// Condition for signaling start of thread
boost::condition m_startCondition;
/// T must satisfy the implicit service interface and provide a start and a stop method
T m_serviceObject;
};
La classe pourrait être utilisé comme suit:
class TestObject3
{
public:
TestObject3()
:m_work(m_ioService),
m_timer(m_ioService, boost::posix_time::milliseconds(200))
{
m_timer.async_wait(boost::bind(&TestObject3::doWork, this, boost::asio::placeholders::error));
}
void start()
{
// simple event loop
m_ioService.run();
}
void stop()
{
// signal end of event loop
m_ioService.stop();
}
void doWork(const boost::system::error_code& e)
{
// Do some work here
if (e != boost::asio::error::operation_aborted)
{
m_timer.expires_from_now(boost::posix_time::milliseconds(200));
m_timer.async_wait(boost::bind(&TestObject3::doWork, this, boost::asio::placeholders::error));
}
}
private:
boost::asio::io_service m_ioService;
boost::asio::io_service::work m_work;
boost::asio::deadline_timer m_timer;
};
maintenant à mes questions spécifiques:
1) L'utilisation du boost variable d'état correcte? Cela me semble un peu un hack: je voulais attendre que le thread soit lancé donc j'ai attendu sur la variable de condition. Puis, une fois que le nouveau thread a été lancé dans la méthode principale, j'attends à nouveau sur la même variable de condition pour permettre au thread initial de continuer. Ensuite, une fois que la méthode de démarrage du thread initial est terminée, le nouveau thread peut continuer. Est-ce correct?
2) Y a-t-il des cas dans lesquels le thread ne serait pas lancé avec succès par le système d'exploitation? Je me souviens d'avoir lu quelque part que cela pouvait se produire. Si cela est possible, je devrais plutôt faire une attente temporisée sur la variable de condition (comme cela est commenté dans la méthode start)? 3) Je suis conscient que de la classe modèle ne pourrait pas implémenter la méthode d'arrêt "correctement" ie si la boucle d'événement ne s'arrête pas, le code bloquera sur les jointures (soit dans le stop ou dans le destructeur) Je ne vois aucun moyen de contourner cela. Je suppose que c'est à l'utilisateur de la classe de s'assurer que la méthode start et stop est implémentée correctement?
4) J'apprécierais d'autres erreurs de conception, améliorations, etc.
Merci!