gpt4 book ai didi

c++ - 与线程相关的事件对象设计问题(c++ boost)

转载 作者:太空宇宙 更新时间:2023-11-04 14:25:30 33 4
gpt4 key购买 nike

我想要一些关于下面列出的 IService 类的反馈。据我所知,这种类型的类与“事件对象”模式有关。如果我不正确地使用任何相关术语,请原谅/更正。基本上这个想法是,使用这个事件对象类的类需要提供一个开始和一个停止方法来控制一些事件循环。这个事件循环可以用 while 循环或 boost asio 等来实现。

此类负责以非阻塞方式启动新线程,以便事件可以在新线程中/由新线程处理。它还必须处理所有与清理相关的代码。我首先尝试了一种 OO 方法,其中子类负责覆盖方法来控制事件循环,但清理工作很困惑:在调用类没有手动调用的情况下,在析构函数中调用停止方法导致纯虚函数调用停止方法。模板化的解决方案似乎更简洁:

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)boost条件变量的使用是否正确?对我来说这似乎有点麻烦:我想等待线程启动,所以我等待条件变量。然后,一旦新线程在 main 方法中启动,我再次等待相同的条件变量以允许初始线程继续。那么一旦初始线程的start方法退出,新的线程就可以继续了。这样可以吗?

2) 是否存在操作系统无法成功启动线程的情况?我记得在某处读到这可能会发生。如果可能的话,我宁愿对条件变量进行定时等待(正如在 start 方法中注释掉的那样)?

3) 我知道模板类无法“正确”实现停止方法,即如果事件循环未能停止,代码将阻塞连接(在停止或析构函数中)但我没有办法解决这个问题。我想应该由类的用户来确保启动和停止方法得到正确实现?

4) 如果有任何其他设计错误、改进等,我将不胜感激?

谢谢!

最佳答案

最后确定了以下内容:

1) 经过多次测试,条件变量的使用似乎没问题

2) 这个问题还没有出现(还)

3)模板类实现必须满足要求,单元测试用于正确性测试

4) 改进

  • 添加了带锁的连接
  • 在派生线程中捕获异常并在主线程中重新抛出以避免崩溃并且不丢失异常信息
  • 使用 boost::system::error_code 将错误代码反馈给调用者
  • 实现对象是可设置的

代码:

template <typename T>
class IService : private boost::noncopyable
{
typedef boost::shared_ptr<boost::thread> thread_ptr;
typedef T ServiceImpl;
public:
typedef boost::shared_ptr<IService<T> > ptr;

IService()
:m_pServiceObject(&m_serviceObject)
{
}

~IService()
{
/// try stop the service in case it's running
if (m_pServiceThread && m_pServiceThread->joinable())
{
stop();
}
}

static ptr create()
{
return boost::make_shared<IService<T> >();
}

/// Accessor to service implementation. The handle can be used to configure the implementation object
ServiceImpl& get() { return m_serviceObject; }
/// Mutator to service implementation. The handle can be used to configure the implementation object
void set(ServiceImpl rServiceImpl)
{
// the implementation object cannot be modified once the thread has been created
assert(m_pServiceThread == 0);
m_serviceObject = rServiceImpl;
m_pServiceObject = &m_serviceObject;
}

void set(ServiceImpl* pServiceImpl)
{
// the implementation object cannot be modified once the thread has been created
assert(m_pServiceThread == 0);

// make sure service object is valid
if (pServiceImpl)
m_pServiceObject = pServiceImpl;
}

/// if the service implementation reports an error from the start or stop method call, it can be accessed via this method
/// NB: only the last error can be accessed
boost::system::error_code getServiceErrorCode() const { return m_ecService; }

/// The join method allows the caller to block until thread completion
void join()
{
// protect this method from being called twice (e.g. by user and by stop)
boost::mutex::scoped_lock lock(m_joinMutex);
if (m_pServiceThread && m_pServiceThread->joinable())
{
m_pServiceThread->join();
m_pServiceThread.reset();
}
}

/// This method launches the non-blocking service
boost::system::error_code start()
{
boost::mutex::scoped_lock lock(m_threadMutex);

if (m_pServiceThread && m_pServiceThread->joinable())
{
// already running
return boost::system::error_code(SHARED_INVALID_STATE, shared_category);
}

m_pServiceThread = thread_ptr(new boost::thread(boost::bind(&IService2::main, this)));
// Wait for condition to be signaled
m_startCondition.wait(m_threadMutex);

// notify main to continue: it's blocked on the same condition var
m_startCondition.notify_one();
// No error
return boost::system::error_code();
}

/// This method stops the non-blocking service
boost::system::error_code stop()
{
// trigger the stopping of the event loop
//boost::system::error_code ec = m_serviceObject.stop();
assert(m_pServiceObject);
boost::system::error_code ec = m_pServiceObject->stop();
if (ec)
{
m_ecService = ec;
return ec;
}

// The service implementation can return an error code here for more information
// However it is the responsibility of the implementation to stop the service event loop (if running)
// Failure to do so, will result in a block
// If this occurs in practice, we may consider a timed join?
join();

// If exception was thrown in new thread, rethrow it.
// Should the template implementation class want to avoid this, it should catch the exception
// in its start method and then return and error code instead
if( m_exception )
boost::rethrow_exception(m_exception);

return ec;
}

private:
/// runs in it's own thread
void main()
{
try
{
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 implementation of event loop
// This will block
// In scenarios where the service fails to start, the implementation can return an error code
m_ecService = m_pServiceObject->start();

m_exception = boost::exception_ptr();
}
catch (...)
{
m_exception = boost::current_exception();
}
}

/// Service thread
thread_ptr m_pServiceThread;
/// Thread mutex
mutable boost::mutex m_threadMutex;
/// Join mutex
mutable boost::mutex m_joinMutex;
/// 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;
T* m_pServiceObject;
// Error code for service implementation errors
boost::system::error_code m_ecService;

// Exception ptr to transport exception across different threads
boost::exception_ptr m_exception;
};

当然欢迎进一步的反馈/批评。

关于c++ - 与线程相关的事件对象设计问题(c++ boost),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4278072/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com