linux上c++线程间的线程安全数据交换/共享内存
thread safe data exchange between threads/shared memory in C++ on linux
我有点困惑:在生产中,我们有两个进程通过共享内存进行通信,数据交换的一部分是long类型和bool类型。对该数据的访问不同步。很长一段时间以来,它一直工作良好,现在仍然如此。我知道修改一个值不是原子性的,但是考虑到这些值被修改/访问了数百万次,这一定会失败吗?
下面是一个示例代码,它在两个线程之间交换一个数字:#include <pthread.h>
#include <xmmintrin.h>
typedef unsigned long long uint64;
const uint64 ITERATIONS = 500LL * 1000LL * 1000LL;
//volatile uint64 s1 = 0;
//volatile uint64 s2 = 0;
uint64 s1 = 0;
uint64 s2 = 0;
void* run(void*)
{
register uint64 value = s2;
while (true)
{
while (value == s1)
{
_mm_pause();// busy spin
}
//value = __sync_add_and_fetch(&s2, 1);
value = ++s2;
}
}
int main (int argc, char *argv[])
{
pthread_t threads[1];
pthread_create(&threads[0], NULL, run, NULL);
register uint64 value = s1;
while (s1 < ITERATIONS)
{
while (s2 != value)
{
_mm_pause();// busy spin
}
//value = __sync_add_and_fetch(&s1, 1);
value = ++s1;
}
}
正如你所看到的,我已经注释掉了一些东西:
//volatile uint64 s1 = 0;
和
//值= __sync_add_and_fetch(及s1, 1),
__sync_add_and_fetch自动递增一个变量
我知道这不是很科学,但是在没有同步功能的情况下运行几次,它完全可以工作。此外,如果我测量两个版本同步和不同步,他们运行在相同的速度,为什么__sync_add_and_fetch不增加任何开销?
我的猜测是编译器保证了这些操作的原子性,因此我在生产中没有看到问题。但是仍然不能解释为什么__sync_add_and_fetch没有增加任何开销(即使在调试中运行)。
关于矿山环境的更多细节:Ubuntu 10.04, gcc4.4.3Intel i5多核cpu。
生产环境是类似的,只是运行在更强大的CPU和Centos操作系统上。
谢谢你的帮助
基本上你是在问"为什么我看不出
s2++;
和
__sync_add_and_fetch(&s2, 1);
好吧,如果你去看看编译器在这两种情况下生成的实际代码,你会发现有一个区别——s2++
版本将有一个简单的INC指令(或者可能是一个ADD),而__sync
版本将在该指令上有一个LOCK前缀。
那么为什么没有LOCK前缀也可以工作呢?虽然通常情况下,LOCK前缀对于任何基于x86的系统都是必需的,但对于您的系统却不需要。对于基于Intel Core的芯片,LOCK只需要在总线上的不同cpu之间进行同步。当在单个CPU上运行时(即使有多个内核),它在没有CPU的情况下进行内部同步。
那么为什么在__sync
的情况下你没有看到放缓?好吧,酷睿i7是一个"有限"的芯片,因为它只支持单插槽系统,所以你不能有多个cpu。这意味着永远不需要LOCK,实际上CPU完全忽略了它。现在代码变大了1字节,这意味着如果你的读取或解码受到限制,它可能会产生影响,但你没有,所以你看不出有什么不同。
如果您在多套接字Xeon系统上运行,您将看到LOCK前缀的(小)减速,并且在非LOCK版本中也可能看到(罕见的)失败。
我认为编译器不会产生原子性,除非你使用一些特定于编译器的模式,所以这是不允许的。
如果只有两个进程在使用共享内存,通常不会发生问题,特别是如果代码段足够短。操作系统倾向于阻塞一个进程,并在最佳情况下运行另一个进程(例如I/O),因此它将运行一个进程到一个良好的隔离点,然后切换到下一个。
尝试运行相同应用程序的几个实例,看看会发生什么。
我看到你正在使用Martin Thompson线程间延迟的例子。
我的猜测是编译器保证了这些操作的原子性,因此我在生产中没有看到问题。但是仍然不能解释为什么__sync_add_and_fetch没有增加任何开销(即使在调试中运行)。
编译器在这里不保证任何东西。您正在运行的X86平台是。这段代码可能会在时髦的硬件上失败。
不知道你在做什么,但是c++ 11确实通过std::atomic提供了原子性。您还可以查看boost::atomic。我假设您对Disruptor模式感兴趣,我将无耻地将我的移植到c++中称为Disruptor——.
- 从不同线程使用int64的不同字节安全吗
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- 在C++中使用cURL和多线程
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 在cuda线程之间共享大量常量数据
- 如何将元素添加到数组的线程安全函数?
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- Qt C++静态thread_local QNetworkAccessManager是线程应用程序的好选择吗
- 异常属于C++中的线程还是进程
- C++中的线程安全删除
- 在多线程环境中交换C 地图对象
- 如何在多线程 c++ 17 程序中交换两个指针
- 如何使用QThreads使无锁生产者-消费者线程交换更加异常安全
- shared_ptr交换线程安全吗
- 如何使C++映射::交换线程安全
- 这是交换(多线程)的异常安全实现吗
- linux上c++线程间的线程安全数据交换/共享内存
- 线程创建和交换槽上的数据
- 在多线程环境中使用auto_ptr交换对象是安全的,不需要锁定