最令人费解的 C++ 堆分配错误
Most puzzling C++ heap allocation bug
我无法 post 源代码,但我可以在概念层面上解释其中的一部分,希望能帮助我理解为什么我的解决方案有效。
我的应用程序有 3 个线程:A、B 和 C(主线程)。
线程 B 有一个 Foo 对象列表。
每个Foo对象恰好包含1个Mutex对象,它是对递归互斥体的包装,以及一堆用于以同步方式设置和获取各种属性的方法,使用Mutex和2个方法用于设置和获取 markedForDelete 属性。
线程 B 所做的就是使用迭代器遍历所述列表,并删除标记为删除的 Foo 对象,否则执行其他指令。它是唯一负责使用类似于以下的基本代码销毁 Foo 对象的线程:
while (running)
{
fooListLock->Lock();
for (vector<Foo*>::iterator it = fooList.begin(); it)
{
if (it->isMarkedForDelete())
{
it = fooList.erase(it);
}
else
{
it->execute();
}
}
fooListLock->Unlock();
sleep (sleepVariable);
}
线程 A 和 C 将创建 Foo 对象并将其添加到列表中,它们还可以将它们标记为要删除,这是使用其他互斥锁以同步方式完成的。
线程 C 偶尔会关闭,之后总是会重新启动,但以受控方式重新启动,绝不会在内存分配/释放期间,并且始终会释放锁定的互斥量。
问题在于,当 Foo 的 Mutex 在堆内存中分配时(通过 new
运算符),应用程序将进入死锁状态,线程 C 想要访问线程 A 锁定的资源,而前者想要访问被线程 B 锁定的资源,线程 B 被 Foo 的 Mutex 阻塞,后者被锁定但没有所有者。使用 GDB 我发现 Mutex 的 pthread_mutex_t owner 值为 0 或负数,不对应于任何线程的 id。死锁的阻塞结束发生在线程B中的这段代码:if (it->isMarkedForDelete())
.
我非常直观的解决方案是在堆栈上分配 Foo 的 Mutex,它无需任何其他修改即可工作!应用程序永远不会以这种方式达到死锁状态。
编译是使用设置了 O2 标志的 g++ 4.8 完成的。
我知道继续进行下去并不多,但有人可以帮助我理解为什么我的解决方案有效吗?
我当然相信,这与堆错误无关。最有可能的是,您没有正确初始化互斥体。你打电话给 pthread_mutex_initialize 吗?
我无法 post 源代码,但我可以在概念层面上解释其中的一部分,希望能帮助我理解为什么我的解决方案有效。
我的应用程序有 3 个线程:A、B 和 C(主线程)。
线程 B 有一个 Foo 对象列表。
每个Foo对象恰好包含1个Mutex对象,它是对递归互斥体的包装,以及一堆用于以同步方式设置和获取各种属性的方法,使用Mutex和2个方法用于设置和获取 markedForDelete 属性。
线程 B 所做的就是使用迭代器遍历所述列表,并删除标记为删除的 Foo 对象,否则执行其他指令。它是唯一负责使用类似于以下的基本代码销毁 Foo 对象的线程:
while (running)
{
fooListLock->Lock();
for (vector<Foo*>::iterator it = fooList.begin(); it)
{
if (it->isMarkedForDelete())
{
it = fooList.erase(it);
}
else
{
it->execute();
}
}
fooListLock->Unlock();
sleep (sleepVariable);
}
线程 A 和 C 将创建 Foo 对象并将其添加到列表中,它们还可以将它们标记为要删除,这是使用其他互斥锁以同步方式完成的。
线程 C 偶尔会关闭,之后总是会重新启动,但以受控方式重新启动,绝不会在内存分配/释放期间,并且始终会释放锁定的互斥量。
问题在于,当 Foo 的 Mutex 在堆内存中分配时(通过 new
运算符),应用程序将进入死锁状态,线程 C 想要访问线程 A 锁定的资源,而前者想要访问被线程 B 锁定的资源,线程 B 被 Foo 的 Mutex 阻塞,后者被锁定但没有所有者。使用 GDB 我发现 Mutex 的 pthread_mutex_t owner 值为 0 或负数,不对应于任何线程的 id。死锁的阻塞结束发生在线程B中的这段代码:if (it->isMarkedForDelete())
.
我非常直观的解决方案是在堆栈上分配 Foo 的 Mutex,它无需任何其他修改即可工作!应用程序永远不会以这种方式达到死锁状态。
编译是使用设置了 O2 标志的 g++ 4.8 完成的。
我知道继续进行下去并不多,但有人可以帮助我理解为什么我的解决方案有效吗?
我当然相信,这与堆错误无关。最有可能的是,您没有正确初始化互斥体。你打电话给 pthread_mutex_initialize 吗?