【发布时间】:2011-03-18 21:36:38
【问题描述】:
谁能提供一个或多个具体示例,其中 RAII 不是最有效的资源管理方法,为什么?
【问题讨论】:
-
直到现在还没有找到。很高兴得到启发。
标签: c++ raii resource-management
谁能提供一个或多个具体示例,其中 RAII 不是最有效的资源管理方法,为什么?
【问题讨论】:
标签: c++ raii resource-management
我能想到 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)使用operator, 甚至可以使用RAII,但我从未真正想到过。但我想这不是很自然:
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
}
}
【讨论】:
operator, 的使用是由 5.18[expr.comma]/1 保证的:“左表达式的所有副作用 (1.9),除了破坏temporaries (12.2),在评估正确的表达式之前执行。"
while (true) { { lock l(mutex); if (exit) break; } ... } 再次使用 RAII,并且比operator, 使用更容易阅读。这与您的建议非常相似,因为它们将检查移到循环条件之外,以便可以将其包含在自己的范围内。
有时需要两阶段初始化(创建、初始化、使用)。
甚至是三阶段:在我们的产品中,有一组独立对象,每个对象都运行一个线程,并且能够通过优先级继承队列订阅任意数量的其他对象(包括它自己)。启动时从配置文件中读取对象及其订阅。在构造时,每个对象 RAII 是它可以获取的所有内容(文件、套接字等),但没有对象可以订阅其他对象,因为它们是以未知的顺序构造的。因此,在构建完所有对象之后,第二阶段是建立所有连接,第三阶段是在建立所有连接后,释放线程并开始消息传递。同样,关闭也是多阶段的。
【讨论】:
GC 可以为程序员处理循环数据结构的内存,而 RAII 则需要程序员在某处手动中断循环。
【讨论】:
shared_ptr 和后向 weak_ptr 保存一个双链表,并且您都将使用 RAII 并且不会遇到循环问题。
RAII 意味着资源的所有权是通过语言构造提供的保证来定义和管理的,最明显的是但不限于构造函数和析构函数。
C++ 中 RAII 的意义在于资源所有权政策实际上可以由语言强制执行。 RAII 的一个较小的替代方案是 API 建议调用者(例如,通过 cmets 或其他文档)在特定时间显式执行 ACQUIRE() 和 RELEASE() 操作。这种政策不能由语言强制执行。
因此,最初的问题是另一种方式来询问是否存在无法执行的资源管理方法比 RAII 更可取的情况。 我能想到的唯一情况是您故意规避该语言中现有的资源管理结构,并编写自己的框架。例如,您正在实现一个垃圾收集脚本语言解释器。原子的“虚拟分配”可能会与内存块玩游戏。同样,基于池的分配器希望程序最终调用DESTROY_POOL() 操作,并产生全局后果(即,从该池分配的任何项目都将失效)。
【讨论】:
在资源释放可能失败的情况下,RAII 可能不足以管理该资源(因为析构函数不应该抛出)。不过,RAII 可能仍然是该解决方案的一部分。
【讨论】: