我创建了一个围绕 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;
};
我在我的主应用程序类中保留了一个 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;
};
然后我的pushTask
方法重载,如下所示:
void pushTask( std::weak_ptr<VoidFunc> f )
{
mService.post( CancelableTask(f) );
mService.reset();
}
然后我使用以下方法将可取消的任务发布到队列中:
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 ) );
或者VoidFunc
如果您愿意,可以使用 typedef:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
mUiTaskQueue->pushTask( std::weak_ptr<VoidFunc>( mTask ) );
只要我保持shared_ptr
tomTask
左右,io_service
就会执行任务。如果我打电话reset
,mTask
则weak_ptr
无法锁定,并且根据需要跳过任务。
我的问题实际上是对所有这些新工具的信心:是new std::function<void(void)>( std::bind( ... ) )
一件可以做的事情,并且使用shared_ptr
?