2011-12-29 23 views
5

我正在使用valgrind运行多线程套接字程序。客户端将通过TCP向服务器发送请求,然后忙于等待布尔值。当调用服务于服务器响应的回调函数时,布尔值将被设置。一旦接收到响应(并设置了布尔标志),服务器将再次发出请求,并在循环中重复执行此操作。我知道对共享变量(布尔值)的非同步访问可能会导致线程问题,但我尝试过使用pthread互斥体,并且程序减速了大约20%(这里速度很重要)。我相信写入共享布尔变量是好的,因为它可以在一个周期内完成。valgrind在多线程套接字程序中失速

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

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

+1

忙于等待共享布尔变量不是“在单个循环中完成”,它在每个循环迭代中以几个周期完成,并且如果您的忙循环等待TCP网络上的往返行程,则循环可能会迭代数十亿次(因此浪费数十亿个CPU周期,可能在其他地方更好使用)。比你提到的任何一个更好的解决方案是等待一个条件变量,并让回调函数发出条件变量的信号,以在数据准备就绪时唤醒你的线程。 – 2011-12-29 01:48:26

+0

我在说写入布尔变量是在一个周期内完成的(而不是整个繁忙的等待过程)。话虽如此,我应该说写入布尔变量是自动完成的(因为缓存未命中等可以推动单个字节的写入超过单个CPU周期) – Taras 2011-12-29 05:18:35

+0

什么Jeremy说 - 忙等待是一个坏主意,条件变量是更好的,并且不太可能会变慢... – BillT 2014-03-31 17:22:30

回答

6

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

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

Valgrind可能对您的应用程序做了什么,正在改变线程之间的时序和交互方式,可能会暴露代码中的错误和竞态条件,您在单独运行时通常不会看到这些错误和竞争条件。

您应用相同的逻辑来判断您使用的开源代码线程框架中的错误不适用于我认为的Valgrind。我建议您将这些挂起视为代码中的错误并对其进行调试,因为这很可能是它们的原因。

作为一个方面说明,使用互斥对您描述的问题可能是过度的。您应该调查信号量或条件变量。

祝你好运。

+0

不完全相信我的代码中存在错误,但是您的文章被记录下来。附注 - 我使用pthread库查看条件变量,并且这些需要互斥体来停止实际条件本身的竞争条件。 – Taras 2011-12-29 22:21:33

+1

@Taras:正确的条件变量。在这种情况下,信号将成为我的首选。比互斥体轻得多。在挂起的问题上,为什么不用gdb检查挂起的进程以找出它在做什么? – Miguel 2011-12-30 00:30:34

3

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

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

+0

答案确实表明,对于大多数写入来说,布尔值是原子的:“CPU通常以原子方式读取和写入单个字节。”从问题的答案来看,问题不是原子性,而是所有CPU都看到新值的时候。 – Taras 2011-12-29 05:20:38

2

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

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

+0

这两个线程之间唯一共享的是布尔标志(它被用作信号)。繁忙的等待线程只有在标志已设置时才会唤醒,这意味着标志中的更改必须传播到正在运行繁忙等待线程的CPU。鉴于此,所描述的代码是否仍然无法正常工作? – Taras 2011-12-29 05:23:17

+0

嘿,你有信号了吗?这可能会诀窍。 :) – sarnold 2011-12-29 05:28:07