正在使用shared_ptr和weak_ptr来管理std::函数安全的生命周期
Is using shared_ptr and weak_ptr to manage lifetime of std::function safe?
我在boost::asio::io_service周围创建了一个包装器来处理OpenGL应用程序的GUI线程上的异步任务。
任务可以从其他线程创建,因此boost::asio
似乎非常适合此目的,并且意味着我不需要编写带有相关互斥锁和锁定的自己的任务队列。我想保持每帧的工作低于可接受的阈值(例如5毫秒),所以我调用poll_one
,直到超出预期的预算,而不是调用run
。据我所知,这要求我在发布新任务时调用reset
,这似乎工作得很好。
由于它很短,下面是全部内容,sans #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
处理的,我不确定如何最好地做到这一点。
我已经尝试了包装任何任务,我可能想要在shared_ptr
中取消,并制作一个包装器对象,该对象将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
类型定义:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
mUiTaskQueue->pushTask( std::weak_ptr<VoidFunc>( mTask ) );
只要我保持shared_ptr
到mTask
左右,那么io_service
将执行任务。如果我在mTask
上调用reset
,那么weak_ptr
无法锁定,任务将按预期跳过。
我的问题是对所有这些新工具的信心:new std::function<void(void)>( std::bind( ... ) )
是一件可以做的事情吗?用shared_ptr
管理是一件安全的事情吗?
是的,这是安全的。
对于代码:
VoidFunc *task = new VoidFunc( std::bind(&MyApp::doUiTask, this) );
mTask = std::shared_ptr<VoidFunc>( task );
只做:
mTask.reset(new VoidFunc( std::bind(&MyApp::doUiTask, this) ) );
(及其他地方)。
请记住,您需要处理竞争条件,在您重置shared_ptr之前,一个线程可能会在weak_ptr上获得锁,从而使回调保持活动状态,因此您偶尔会看到回调,即使您沿着代码路径重置了shared_ptr回调。
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- 引用 std::shared:ptr 以避免引用计数
- C++14 unique_ptr并使用已删除的函数'std::unique-ptr' unique_ptr错误
- 使用std :: String ptr的错误打印std :: String
- C++中的大小释放:全局运算符delete的正确行为是什么(void*ptr,std::size_t size)
- std::哈希表示无序映射中的唯一 PTR
- boost::shared_ptr和std::shared-ptr的同居
- 我可以用std::shared_ptr而不是boost::shared-ptr构建boost库吗
- 正确使用std智能指针以确保ptr安全
- 如何创建 std::string 包装器,它将 ptr 保留为 std::string 和 ptr 到创建该包装器实例的
- 为什么 std::string{ "const char ptr" } 有效?
- 为什么 gcc 4.9.0 中没有定义"void operator delete(void* ptr, std::size_t size) noexcept;"?
- 在 std::map 的值中使用非 ptr 是一种很好的做法吗
- 共享 PTR - C++:std::shared_ptr<T> 和 std::shared_ptr<T const> 有什么区别?