Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+中线程/共享内存之间的线程安全数据交换+;在linux上_C++_Linux_Multithreading_Gcc_Atomic - Fatal编程技术网

C++ C+中线程/共享内存之间的线程安全数据交换+;在linux上

C++ C+中线程/共享内存之间的线程安全数据交换+;在linux上,c++,linux,multithreading,gcc,atomic,C++,Linux,Multithreading,Gcc,Atomic,我有点困惑: 在生产中,我们有两个进程通过共享内存进行通信,数据交换的一部分是一个长而复杂的过程。对该数据的访问未同步。很长一段时间以来,它一直运转良好,现在仍然如此。我知道修改一个值不是原子性的,但是考虑到这些值被修改/访问了数百万次,这必须失败吗 下面是一段示例代码,它在两个线程之间交换一个数字: #include <pthread.h> #include <xmmintrin.h> typedef unsigned long long uint64; const

我有点困惑: 在生产中,我们有两个进程通过共享内存进行通信,数据交换的一部分是一个长而复杂的过程。对该数据的访问未同步。很长一段时间以来,它一直运转良好,现在仍然如此。我知道修改一个值不是原子性的,但是考虑到这些值被修改/访问了数百万次,这必须失败吗

下面是一段示例代码,它在两个线程之间交换一个数字:

#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;
      }
}
#包括
#包括
typedef无符号长uint64;
const uint64迭代次数=500LL*1000LL*1000LL;
//挥发性uint64 s1=0;
//挥发性uint64 s2=0;
uint64 s1=0;
uint64 s2=0;
void*运行(void*)
{
寄存器uint64值=s2;
while(true)
{
while(值==s1)
{
_mm_pause();//忙旋转
}
//值=\uuuuu sync\u add\u和\u fetch(&s2,1);
值=++s2;
}
}
int main(int argc,char*argv[])
{
pthread_t线程[1];
pthread_创建(线程[0],NULL,run,NULL);
寄存器uint64值=s1;
while(s1<迭代次数)
{
while(s2!=值)
{
_mm_pause();//忙旋转
}
//值=\uuuuu sync\u add\u和\u fetch(&s1,1);
值=++s1;
}
}
正如你所看到的,我已经注释掉了两件事:

//挥发性uint64 s1=0

//值=\uuuuu sync\u add\u和\u fetch(&s1,1)

__sync_add_和_fetch以原子方式递增变量

我知道这不是很科学,但在没有同步功能的情况下运行了几次,效果非常好。此外,如果我测量两个版本的同步和不同步,它们以相同的速度运行,为什么uuu sync u add u和fetch没有增加任何开销

我的猜测是编译器保证了这些操作的原子性,因此我认为在生产中没有问题。但仍然无法解释为什么uuu sync_add_和_fetch没有增加任何开销(甚至在调试中运行)

有关矿山环境的更多详细信息: ubuntu 10.04,gcc4.4.3 英特尔i5多核处理器

生产环境与此类似,它只是在更强大的CPU和Centos上运行


感谢您的帮助

我认为在您使用某些特定于编译器的模式之前,编译器不会生成原子性,所以这是不可能的

如果只有两个进程使用共享内存,通常不会出现问题,特别是如果代码段足够短的话。操作系统更喜欢阻塞一个进程,并在其最佳状态(例如I/O)下运行另一个进程,因此它会将一个进程运行到一个良好的隔离点,然后切换到下一个


尝试运行同一应用程序的几个实例,看看会发生什么。

我看到您使用的是Martin Thompson线程间延迟示例

我的猜测是编译器保证了这些操作的原子性,因此我认为在生产中没有问题。但仍然无法解释为什么uuu sync_add_和_fetch没有增加任何开销(甚至在调试中运行)

编译器在这里不保证任何东西。您正在运行的X86平台是。这段代码在时髦的硬件上可能会失败

不确定您在做什么,但C++11确实提供了std::atomic的原子性。你也可以看看。我假设你对破坏模式感兴趣,我会无耻地将我的端口插入C++调用。

基本上你问“为什么我看不到行为/性能上的差异?”
s2++;

好的,如果你去看看编译器在这两种情况下生成的实际代码,你会发现有一个区别,
s2++
版本将有一个简单的INC指令(或者可能有一个ADD),而
\u sync
版本将在该指令上有一个锁前缀

那么,为什么它不带锁前缀就可以工作呢?一般来说,在任何基于x86的系统上都需要锁前缀,但事实证明,您的系统不需要锁前缀。对于基于Intel Core的芯片,锁只需要通过总线在不同CPU之间同步。在单CPU上运行时(即使使用多核),它在没有它的情况下执行其内部同步

那么,为什么在
\uu sync
的情况下,你看不到任何减速?好吧,Core i7是一个“有限”的芯片,因为它只支持单插槽系统,所以你不能有多个CPU。这意味着永远不需要锁,事实上CPU完全忽略了锁。现在代码大了1字节,这意味着如果你需要锁,它可能会产生影响ifetch或decode有限,但你不是,所以你看不出有什么不同


如果您要在多套接字Xeon系统上运行,您会看到锁前缀的速度(稍微)减慢,并且还会看到(很少)非锁定版本中的故障。

如果
++
不是原子的,您希望从这段代码中看到什么故障症状?好吧,如果一个线程正在读取,而另一个线程正在同时写入相同的内容,并且它没有同步,这是未定义的行为,因此我预期会发生崩溃,或者一些随机值,这是我所不知道的当然,这是错误的,我需要同步,我会睡得更好…这里也解释了,只是再次检查,我们的产品是在Xeon cpu上:)
__sync_add_and_fetch(&s2, 1);