Ran*_*Etc 5 c++ boost-thread shared-ptr boost-asio std-function
我围绕boost :: asio :: io_service创建了一个包装器,以处理OpenGL应用程序的GUI线程上的异步任务。
任务可能是从其他线程创建的,因此boost::asio看来是理想的选择,这意味着我不需要编写带有关联互斥锁和锁定的任务队列。我想将每个帧上完成的工作保持在可接受的阈值(例如5毫秒)以下,因此我要打电话poll_one直到超出所需的预算为止,而不是打电话run。据我所知,这要求我reset在发布新任务时都打电话给我,这似乎效果很好。
由于很短,所以这里是整件事,无#include:
typedef std::function<void(void)> VoidFunc;
typedef std::shared_ptr<class UiTaskQueue> UiTaskQueueRef;
class UiTaskQueue {
public:
static UiTaskQueueRef create()
{
return UiTaskQueueRef( new UiTaskQueue() );
}
~UiTaskQueue() {}
// normally just hand off the results of std/boost::bind to this function:
void pushTask( VoidFunc f )
{
mService.post( f );
mService.reset();
}
// called from UI thread; defaults to ~5ms budget (but always does one call)
void update( const float &budgetSeconds = 0.005f )
{
// getElapsedSeconds is a utility function from the GUI lib I'm using
const float t = getElapsedSeconds();
while ( mService.poll_one() && getElapsedSeconds() - t < budgetSeconds );
}
private:
UiTaskQueue() {}
boost::asio::io_service mService;
};
Run Code Online (Sandbox Code Playgroud)
我在主应用程序类中保留一个UiTaskQueueRef实例,并mUiTaskQueue->update()从应用程序的动画循环中调用。
我想扩展此类的功能以允许取消任务。我以前的实现(使用几乎相同的接口)为每个任务返回了一个数字ID,并允许使用该ID取消任务。但是现在boost::asio我不确定如何最好地执行队列管理和相关联的锁定。
我通过将可能要取消的所有任务包装在a中,shared_ptr并制作了一个包装对象来进行尝试,该包装对象将a存储weak_ptr到任务并实现()运算符,以便可以将其传递给io_service。看起来像这样:
struct CancelableTask {
CancelableTask( std::weak_ptr<VoidFunc> f ): mFunc(f) {}
void operator()(void) const {
std::shared_ptr<VoidFunc> f = mFunc.lock();
if (f) {
(*f)();
}
}
std::weak_ptr<VoidFunc> mFunc;
};
Run Code Online (Sandbox Code Playgroud)
然后,我的pushTask方法的重载如下所示:
void pushTask( std::weak_ptr<VoidFunc> f )
{
mService.post( CancelableTask(f) );
mService.reset();
}
Run Code Online (Sandbox Code Playgroud)
然后,我使用以下命令将可取消的任务发布到队列中:
std::function<void(void)> *task = new std::function<void(void)>( boost::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr< std::function<void(void)> >( task );
mUiTaskQueue->pushTask( std::weak_ptr< std::function<void(void)> >( mTask ) );
Run Code Online (Sandbox Code Playgroud)
或使用VoidFunctypedef,如果您愿意:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
mUiTaskQueue->pushTask( std::weak_ptr<VoidFunc>( mTask ) );
Run Code Online (Sandbox Code Playgroud)
只要我保持shared_ptr到mTask周围那么io_service将执行的任务。如果我拨打电话reset,mTask则weak_ptr无法锁定,并且可以根据需要跳过任务。
我的问题确实是对所有这些新工具的信心之一:new std::function<void(void)>( std::bind( ... ) )要做的事是一件好事,而使用a进行管理是一件安全的事shared_ptr吗?
是的,这很安全。
对于代码:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
Run Code Online (Sandbox Code Playgroud)
做就是了:
mTask.reset(new VoidFunc( std::bind(&MyApp::doUiTask, this) ) );
Run Code Online (Sandbox Code Playgroud)
(和其他地方)。
请记住,您需要处理竞争条件,即在您重置shared_ptr以保持回调活动之前,tread可能会锁定weak_ptr,因此,即使您执行了代码,您偶尔也会看到回调重置回调shared_ptr的路径。