我有一个名为Root的类,它充当动态方法调用的某种电话簿:它包含指向对象的url键字典。当一个命令想要执行一个给定的方法时,它会用一个url和一些参数调用一个Root实例:
root_->call("/some/url", ...);实际上,Root中的call方法看起来很接近于此:
// Version 0
const Value call(const Url &url, const Value &val) {
// A. find object
if (!objects_.get(url.path(), &target))
return ErrorValue(NOT_FOUND_ERROR, url.path());
}
// B. trigger the object's method
return target->trigger(val);
}从上面的代码中,您可以看到这个“调用”方法并不是线程安全的:可以在A和B之间删除"objects_“对象,而且我们也不能保证在读取”“成员(字典)时不会更改它。
我想到的第一个解决办法是:
// Version I
const Value call(const Url &url, const Value &val) {
// Lock Root object with a mutex
ScopedLock lock(mutex_);
// A. find object
if (!objects_.get(url.path(), &target))
return ErrorValue(NOT_FOUND_ERROR, url.path());
}
// B. trigger the object's method
return target->trigger(val);
}这很好,直到“target->触发器(Val)”是一个需要通过更改对象的url或插入新对象来更改根的方法。修改作用域和使用RW互斥体可能会有所帮助(读取比在Root上写的要多得多):
// Version II
const Value call(const Url &url, const Value &val) {
// A. find object
{
// Use a RW lock with smaller scope
ScopedRead lock(mutex_);
if (!objects_.get(url.path(), &target))
return ErrorValue(NOT_FOUND_ERROR, url.path());
}
}
// ? What happens to 'target' here ?
// B. trigger the object's method
return target->trigger(val);
}“目标”会怎么样?如何确保它不会在查找和调用之间被删除?
一些想法:对象删除可以在根中的消息队列中发布。但是,我们将需要另一个RW互斥锁删除完整的方法作用域,并使用一个单独的线程来处理删除队列。
这一切对我来说都很复杂,我不知道并行设计是否必须像这样,或者我只是没有正确的想法。
PS:代码是一个名为示波 (OpenSoundControl it)的开源项目的一部分。
发布于 2010-01-21 13:39:57
你这样做是走错路了。记住:你不能锁定数据,你只能阻止代码。您不能用本地定义的互斥保护“对象”成员。您需要在更改对象集合的代码中使用完全相同的互斥对象。当另一个线程执行call()方法时,它必须阻止该代码。互斥必须至少在类范围内定义。
https://stackoverflow.com/questions/2108355
复制相似问题