valgrind 在多线程套接字程序中停止

Posted

技术标签:

【中文标题】valgrind 在多线程套接字程序中停止【英文标题】:valgrind stalls in multithreaded socket program 【发布时间】:2011-12-29 01:36:27 【问题描述】:

我正在使用 valgrind 运行一个多线程套接字程序。客户端将通过 TCP 向服务器发送请求,然后忙于等待布尔值。当调用服务于服务器响应的回调函数时,将设置布尔值。一旦收到响应(并且设置了布尔标志),服务器将再次发出请求,并在循环中重复执行此操作。

我意识到对共享变量(布尔值)的非同步访问会导致线程问题,但我尝试使用 pthread 互斥体,程序速度降低了大约 20%(速度在这里很重要)。我相信写入共享布尔变量是没问题的,因为它可以在一个周期内完成。

该程序在 valgrind 之外运行良好,但在使用 valgrind 运行时经常会停止。我让程序在一夜之间运行..通常需要几秒钟才能完成,所以我认为这不是等待程序完成的时间不够长的情况。线程由开源引擎框架管理(快速修复),所以我认为线程的创建/管理方式没有问题。

有没有人知道 valgrind 在多线程程序/忙等待循环/套接字通信(或这些的组合)方面有任何问题?

【问题讨论】:

对共享布尔变量的忙等待不是“在单个循环中完成”,它在每个循环迭代中在几个循环中完成,如果您的忙循环正在等待 TCP 往返网络,那么循环可能会迭代数十亿次(因此浪费了数十亿 CPU 周期,这些周期可能在其他地方更好地使用)。比您提到的任何一种方法更好的解决方案是等待条件变量,并让回调函数向条件变量发出信号,以便在数据准备好时唤醒您的线程。 我说写入布尔变量是在一个周期内完成的(而不是整个繁忙的等待过程)。话虽如此,我应该声明写入布尔变量是原子完成的(因为缓存未命中等可以推动单个字节的写入超过单个 CPU 周期) Jeremy 说的——忙等待是个坏主意,条件变量更好,而且不太可能变慢...... 【参考方案1】:

虽然其他答案侧重于坚持您采用标准同步方法(我完全同意这一点),但我认为我应该回答您关于 Valgrind 的问题。

据我所知,Valgrind 在多线程环境中运行没有问题。我相信 Valgrind 会强制应用程序在单核上运行,但除此之外它不应该影响您的线程。

Valgrind 对您的应用程序所做的可能是改变线程之间的计时和交互,这可能会暴露代码中的错误和竞争条件,而这些错误和竞争条件在独立运行时通常是看不到的。

在我看来,您用于确定错误不能出现在您使用的开源线程框架中的相同逻辑也适用于 Valgrind。我建议您将这些挂起视为代码中的错误并对其进行调试,因为它们很可能就是这样。

顺便说一句,对于您描述的问题,使用互斥锁可能是矫枉过正。您应该改为研究信号量或条件变量。

祝你好运。

【讨论】:

不完全相信我的代码中存在错误,但您的帖子已被记录。旁注 - 我使用 pthread 库查看了条件变量,这些变量需要互斥锁来停止实际条件本身的竞争条件。 @Taras:在条件变量上正确。在这种情况下,信号量将是我的首选。比互斥锁轻得多。关于挂起的问题,你为什么不用gdb检查一下挂起的进程,看看它在做什么?【参考方案2】:

我刚刚遇到了类似的问题。像 OP 一样,我有一个线程在忙着等待。就我而言,问题在于繁忙的等待占用了几乎所有的 CPU 周期,并导致其他线程的运行速度慢了数千倍。起初我通过在繁忙的等待循环中放置一个usleep(1) 来解决这个问题(仅适用于Valgrind 构建)。然后我阅读了Valgrind 手册并了解了--fair-sched=yes 选项,这也解决了问题并允许我删除usleep(1)

【讨论】:

【参考方案3】:

读取/写入布尔值不是 x86 上的原子操作。

在这里查看我的问题:Is volatile a proper way to make a single byte atomic in C/C++?

【讨论】:

答案确实表明,对于大多数写入布尔值是原子的:“CPU 通常以原子方式读取和写入单个字节。”从问题的答案来看,问题不是原子性,而是所有 CPU 看到新值的时间。【参考方案4】:

即使编写布尔值是一个原子操作,compiler and the CPU are free to re-order the update 也会围绕其他内存访问。您的忙等待线程可能会从忙循环中唤醒,并发现共享数据结构实际上还没有更新。

我强烈建议您坚持使用可供您使用的线程原语来编写一致程序,这些程序每次都能完全按照您的意愿执行。

【讨论】:

两个线程之间唯一共享的是布尔标志(它被用作信号)。只有设置了标志,忙等待线程才会唤醒,这意味着标志中的更改必须已经传播到运行忙等待线程的 CPU。鉴于此,所描述的代码是否仍无法正常运行? 嘿,你尝试过信号了吗?这可能会奏效。 :)

以上是关于valgrind 在多线程套接字程序中停止的主要内容,如果未能解决你的问题,请参考以下文章

valgrind 打印程序调用树+进行多线程性能分析

ZMQ之多线程编程

java多线程之停止线程

gdb:在多线程程序中调用函数而不进行线程

valgrind的callgrind工具进行多线程性能分析

在多线程应用程序中使用 opencv waitKey()