谁能为我提供一个或多个具体示例,其中 RAII不是最有效的资源管理方法,为什么?
5 回答
我能想到 RAII 不是解决方案的唯一情况是多线程关键区域锁管理。通常,建议获取临界区锁(考虑资源)并将其保存在 RAII 对象中:
void push( Element e ) {
lock l(queue_mutex); // acquire on constructing, release on destructing
queue.push(e);
}
但在某些情况下,您不能为此目的使用 RAII。特别是,如果循环条件中使用的变量由多个线程共享,并且您无法为整个循环执行持有锁,那么您必须使用不同的机制获取和释放锁:
void stop_thread() {
lock l(control_mutex);
exit = true;
}
void run() {
control_mutex.acquire();
while ( !exit ) { // exit is a boolean modified somewhere else
control_mutex.release();
// do work
control_mutex.acquire();
}
control_mutex.release();
}
我现在想到的(ab)使用甚至可能使用RAII operator,
,但我从未真正想到过。但我想这不是很自然:
void run() {
while ( lock(control_mutex), !exit ) {
// do work
}
}
所以我想答案不是我能想象的……
编辑:使用 RAII 解决相同问题的其他解决方案:
@马克赎金:
bool should_exit() const {
lock l(mutex);
return exit;
}
void run() {
while ( !should_exit() ) {
// do work
}
}
@fnieto :_
void run() {
while (true) {
{ lock l(mutex);
if (exit) break;
}
// do work
}
}
有时需要两阶段初始化(创建,然后初始化,然后使用)。
甚至是三阶段:在我们的产品中,有一组独立对象,每个对象都运行一个线程,并且能够通过优先级继承队列订阅任意数量的其他对象(包括它自己)。启动时从配置文件中读取对象及其订阅。在构造时,每个对象 RAII 是它可以获取的所有内容(文件、套接字等),但没有对象可以订阅其他对象,因为它们是以未知的顺序构造的。因此,在构建完所有对象之后,第二阶段是建立所有连接,第三阶段是在建立所有连接后,释放线程并开始消息传递。同样,关闭也是多阶段的。
在资源释放可能失败的情况下,RAII 可能不足以管理该资源(因为析构函数不应该抛出)。不过,RAII 可能仍然是该解决方案的一部分。
GC 可以为程序员处理循环数据结构的内存,而 RAII 则需要程序员在某个地方手动中断循环。
RAII 意味着资源的所有权是通过语言构造提供的保证来定义和管理的,最显着的是但不限于构造函数和析构函数。
C++ 中 RAII 的重点是资源所有权策略实际上可以由语言强制执行。RAII 的较小替代方案是 API建议调用者(例如,通过注释或其他文档)在特定时间明确执行ACQUIRE()
和RELEASE()
操作。这种策略不能由语言强制执行。
因此,最初的问题是另一种方式来询问是否存在无法执行的资源管理方法比 RAII 更可取的情况。我能想到的唯一情况是您故意规避该语言中现有的资源管理结构,并编写自己的框架。例如,您正在实现一个垃圾收集脚本语言解释器。原子的“虚拟分配”可能会与内存块玩游戏。类似地,基于池的分配器期望程序最终调用一个DESTROY_POOL()
操作,并产生全局后果(即,从该池分配的任何项目都将无效)。