Я хотел бы получить отзыв о классе IService, указанном ниже. Из того, что я знаю, этот тип класса связан с шаблоном «активный объект». Пожалуйста, извините / исправьте, если я использую любую связанную терминологию неправильно. По сути, идея заключается в том, что классы, использующие этот активный объектный класс, должны предоставлять метод start и stop, который контролирует некоторый цикл событий. Этот цикл событий может быть реализован с помощью цикла while или boost asio и т. Д.
Этот класс отвечает за запуск нового потока неблокирующим образом, чтобы события могли обрабатываться в / новым потоком. Он также должен обрабатывать весь код, связанный с очисткой. Сначала я попробовал подход ОО, в котором подклассы отвечали за переопределение методов для управления циклом событий, но очистка была грязной: в деструкторе вызов метода stop приводил к чисто виртуальному вызову функции в тех случаях, когда вызывающий класс вручную не вызывал остановить метод. Шаблонное решение кажется намного чище:
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;
};
Класс можно использовать следующим образом:
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;
};
Теперь к моим конкретным вопросам:
1) Правильно ли используется переменная условия форсирования? Для меня это немного хакерство: я хотел дождаться запуска потока, поэтому я ждал переменную условия. Затем, после запуска нового потока в методе main, я снова жду той же переменной условия, чтобы позволить исходному потоку продолжить. Затем, после завершения метода запуска начального потока, новый поток может продолжаться. Это нормально?
2) Есть ли случаи, когда поток не будет успешно запущен ОС? Я помню, где-то читал, что это может произойти. Если это возможно, мне лучше подождать время условной переменной (как это закомментировано в методе start)?
3) Мне известно, что шаблонный класс не может правильно реализовать метод stop, т. Е. Если не удается остановить цикл обработки событий, код будет блокироваться в соединениях (либо в остановке, либо в деструкторе), но я не вижу пути обойти это. Я полагаю, что пользователь класса должен убедиться, что метод start и stop реализован правильно?
4) Буду признателен за любые другие ошибки проектирования, улучшения и т. Д.
Спасибо!