当我使用自定义shared_ptr
和Deleter
的{{1}}时,我有一个非常微妙的死锁错误:
首先有一个WorkManager类来管理Work对象:
boost::share_mutex
class Work;
class WorkManager {
public:
static WorkManager& instance() {
static WorkManager instance;
return instance;
}
std::shared_ptr<Work> get(int id);
std::shared_ptr<Work> create(int id);
void update(unsigned int diff);
private:
void _remove(int id, Work* t);
WorkManager();
mutable boost::shared_mutex _mutex;
std::map<int, std::weak_ptr<Work>> _works;
};
void WorkManager::_remove(int id, Work* t)
{
BOOST_ASSERT(t);
std::lock_guard<boost::shared_mutex> lock(_mutex);
_works.erase(id);
delete t;
}
std::shared_ptr<Work> WorkManager::get(int id) {
boost::shared_lock<boost::shared_mutex> lock(_mutex);
auto iter = _works.find(id);
if(iter == _works.end())
return nullptr;
return iter->second.lock();
}
std::shared_ptr<Work> WorkManager::create(int id) {
Work* t = new Work(id);
std::shared_ptr<Work> res;
res.reset(t, std::bind(&WorkManager::_remove, this, t->id(), std::placeholders::_1));
std::lock_guard<boost::shared_mutex> lock(_mutex);
_works[res->id()] = res;
return res;
}
void WorkManager::update(unsigned int diff) {
boost::shared_lock<boost::shared_mutex> lock(_mutex);
for(auto& t : _works)
{
if(std::shared_ptr<Work> work = t.second.lock())
work->update(diff);
}
}
由定时器触发,以100ms的固定间隔调用。由于WorkManager::update()
不包含WorkManager
而只包含shared_ptr<Work>
,因此在weak_ptr<Work>
返回后,work->update(diff);
可能会被销毁,因为可能没有work
持有这个特别的shared_ptr<Work>
了。然后,work
将通过WorkManager::_remove()
的自定义删除工具进行调用。请注意,更新线程已经保持读锁定,在shared_ptr<Work>
中,它需要获取写锁定。但是,它必须在获取此写锁之前释放读锁。所以这个线程已经死了。
这个问题意味着我无法编写任何管理器的功能,例如:
_remove()
如何解决这个问题?我找到了一个解决方案:
boost::shared_lock<boost::shared_mutex> lock(_mutex);
for(auto& t : _works)
{
if(std::shared_ptr<Work> w = t.second.lock())
w->do_something();
}
然而,如果有人忘记了这个技巧,这很容易出错。