C++ 使用共享_ptr和弱_ptr来管理std::function的生命周期安全吗?

C++ 使用共享_ptr和弱_ptr来管理std::function的生命周期安全吗?,c++,boost-asio,shared-ptr,boost-thread,std-function,C++,Boost Asio,Shared Ptr,Boost Thread,Std Function,我已经围绕boost::asio::io_服务创建了一个包装器,用于在OpenGL应用程序的GUI线程上处理异步任务 任务可能是从其他线程创建的,因此boost::asio似乎非常适合此目的,这意味着我不需要编写自己的带有相关互斥体和锁定的任务队列。我希望在每个帧上完成的工作低于可接受的阈值(例如5ms),因此我将调用poll\u one,直到超出所需预算,而不是调用run。据我所知,每当发布新任务时,这需要我调用reset,这似乎工作正常 由于它很短,下面是全部内容,sans#include:

我已经围绕boost::asio::io_服务创建了一个包装器,用于在OpenGL应用程序的GUI线程上处理异步任务

任务可能是从其他线程创建的,因此
boost::asio
似乎非常适合此目的,这意味着我不需要编写自己的带有相关互斥体和锁定的任务队列。我希望在每个帧上完成的工作低于可接受的阈值(例如5ms),因此我将调用
poll\u one
,直到超出所需预算,而不是调用
run
。据我所知,每当发布新任务时,这需要我调用
reset
,这似乎工作正常

由于它很短,下面是全部内容,sans
#include

typedef std::函数VoidFunc;
typedef std::shared_ptr UiTaskQueueRef;
类UiTaskQueue{
公众:
静态UiTaskQueueRef创建()
{
返回UiTaskQueueRef(新的UiTaskQueue());
}
~UiTaskQueue(){}
//通常只需将std/boost::bind的结果传递给此函数:
作废pushTask(作废函数f)
{
m服务员额(f);
mService.reset();
}
//从UI线程调用;默认为~5ms预算(但始终执行一次调用)
无效更新(常量浮动和预算秒=0.005f)
{
//getElapsedSeconds是我正在使用的GUI库中的一个实用函数
const float t=getElapsedSeconds();
while(msservice.poll_one()&&getElapsedSeconds()-t
我在我的主应用程序类中保留一个UiTaskQueueRef实例,并在我的应用程序的动画循环中调用
mUiTaskQueue->update()

我想扩展这个类的功能,允许取消一个任务。我以前的实现(使用几乎相同的接口)为每个任务返回了一个数字ID,并允许使用此ID取消任务。但是现在队列和相关锁定的管理由
boost::asio
处理,我不确定如何最好地做到这一点

我尝试将我可能要取消的任何任务包装到
共享的\u ptr
中,并创建一个包装器对象,该对象将
弱的\u ptr
存储到任务中,并实现
()
操作符,以便将其传递给
io\u服务
。看起来是这样的:

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;
};
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 ) );
只要我将
shared_ptr
保存在
mTask
周围,那么
io_服务将执行该任务。如果我在
mTask
上调用
reset
,则
弱\u ptr
无法锁定,任务将根据需要跳过

我的问题是对所有这些新工具的信心:使用
共享的
管理是安全的吗?

是的,这是安全的

守则:

VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
(和其他地方)


请记住,您需要处理竞态条件,即在您重置共享\u ptr以保持回调活动之前,tread可能会锁定弱\u ptr,因此,即使您沿着代码路径重置回调共享\u ptr,您也会偶尔看到回调。

非常感谢!我想我可以通过从处理任务的UI线程中取消来避免竞争条件,但是我会记住这段代码的线程版本。也感谢初始化提示!
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
mTask.reset(new VoidFunc( std::bind(&MyApp::doUiTask, this) ) );