标签: memory-barriers

删除对象后,未完成的存储会发生什么?

考虑以下简单函数(假设大多数编译器优化关闭)由具有存储缓冲区的 X86 CPU 上不同内核上的两个线程执行:

struct ABC
{
  int x;
  //other members.
};
void dummy(int index)
{
  while(true)
  {
    auto abc = new ABC;
    abc->x = index;
    cout << abc->x;
    // do some other things.
    delete abc;
  }
}
Run Code Online (Sandbox Code Playgroud)

这里,index是线程的索引;1 由线程1 传递,2 由线程2 传递。因此,线程 1 应该始终打印 1,线程 2 应该始终打印 2。

是否存在这样的情况,即存储到x被放入存储缓冲区并在执行delete之后提交?或者是否存在隐式内存屏障来确保在删除之前提交存储?或者一旦遇到删除,任何未完成的存储都会被丢弃?

这变得很重要的情况:

由于delete是将对象的内存返回到空闲列表中(用libc),因此有可能在thread1中刚刚释放的一块内存被thread2中的new操作符返回(不仅是虚拟地址,甚至是返回的底层物理地址可以是相同的)。如果未完成的存储可以在删除后执行,则线程 2 将 abc->x 设置为 2 后,线程 1 中的某些较旧的未完成存储可能会将其覆盖为 1。

这意味着在上面的程序中,thread2可以打印1,这是绝对错误的。线程1和线程2是完全独立的,从程序员的角度来看,线程之间没有数据共享,并且它们不必担心任何同步。

我在这里缺少什么?

c++ x86 multithreading cpu-architecture memory-barriers

3
推荐指数
2
解决办法
197
查看次数

如何通过rust asm构建屏障?

在gcc中,我们可以使用asm volatile("":::"memory");

但我在 rust inline asm 文档中找不到类似于“内存”的选项。

有什么办法可以做到这一点吗?

inline-assembly memory-barriers rust

3
推荐指数
1
解决办法
413
查看次数

Java 易失性内存排序及其在 x86-64 上的编译

考虑以下简单的 Java 应用程序:

public class Main {
    public int a;
    public volatile int b;

    public void thread1(){
        int b;
        a = 1;
        b = this.b;
    }

    public void thread2(){
        int a;
        b = 1;
        a = this.a;
    }

    public static void main(String[] args) throws Exception {
        Main m = new Main();
        while(true){
            m.a = 0;
            m.b = 0;
            Thread t1 = new Thread(() -> m.thread1());
            Thread t2 = new Thread(() -> m.thread2());
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题: …

java x86-64 volatile java-memory-model memory-barriers

3
推荐指数
1
解决办法
277
查看次数

这个内存顺序正确吗?

std::atomic<bool> x{ false };
std::atomic<bool> y{ false };

// thread 1
y.store(true, std::memory_order_seq_cst);
x.store(true, std::memory_order_release);

// thread2
while (!x.load(std::memory_order_relaxed);
assert(y.load(std::memory_order_seq_cst)); // !!!
Run Code Online (Sandbox Code Playgroud)

断言会失败吗?我的理解是:虽然读取x是“放松的”,但一旦“线程2”看到“线程1”的写入,它就看不到yfalse因为写入y发生在写入之前x

内存顺序是从现实生活中的案例复制的,对于这个示例来说可能会更弱,但我没有改变它,以免错过任何微妙之处。

c++ multithreading atomic memory-barriers stdatomic

3
推荐指数
2
解决办法
353
查看次数

C++中的内存屏障/障碍:是否会增强或其他库拥有它们?

我现在正在阅读有关内存屏障和障碍的信息,以此来同步多线程代码并避免代码重新排序.

我通常在Linux OS下用C++开发,我boost大量使用libs,但是我找不到任何与之相关的类.你知道在增强中是否存在栅栏的记忆障碍,或者是否有办法实现相同的概念?如果不是我可以看看哪个好的图书馆?

multithreading boost boost-thread memory-barriers

2
推荐指数
1
解决办法
5343
查看次数

c#struct array的并发性

给定一个struct数组:

public struct Instrument
{
    public double NoS;
    public double Last;
}

var a1 = new Instrument[100];
Run Code Online (Sandbox Code Playgroud)

并且一个线程任务池正在写入这些元素,因为单个元素最多可以同时由两个线程写入,每个双字段一个(有效地通过主题进行上游排队).

并且知道双重可以在64位上原子地写入.(编辑这个错误地说原来是32位)

我需要使用数组中的所有值定期执行计算,并且我希望它们在计算期间保持一致.

所以我可以使用以下方法对数组进

var snapshot = a1.Clone();
Run Code Online (Sandbox Code Playgroud)

现在我的问题是关于同步的具体细节.如果我使成员易变,我认为这根本不会帮助克隆,因为读/写aquire/releases不在数组级别.

现在我可以有一个数组锁,但这会在最常见的数据写入数组的过程中引起很多争论.所以不太理想.

或者我可以有一个每行锁定,但这将是一个真正的痛苦,因为他们都需要在克隆之前被获取,同时我有所有备份的写入.

现在,如果快照没有最新值(如果它只是微秒等)并不重要,所以我想我可能会因为没有锁而逃脱.我唯一担心的是,是否存在持续时间段内没有缓存写回的情况.这是我应该担心的吗?编写器在TPL数据流中,唯一的逻辑是在结构中设置两个字段.我真的不知道函数范围是如何或者是否与缓存回写相关联.

思考/建议吗?

编辑:如果我使用互锁写入结构中的变量怎么样?

edit2:写入量比读取量高很多.写入Nos&Last字段还有两个单独和并发的服务.所以他们可以同时写入.这导致参考对象方法存在原子性问题.

edit3:更多细节.假设数组是30-1000个元素,每个元素可以每秒多次更新.

c# concurrency struct volatile memory-barriers

2
推荐指数
1
解决办法
948
查看次数

如何为TMS320F2812 DSP编写内存屏障?

我查看了TI C/C++编译器v6.1用户指南(spru514e),但没有找到任何内容.

asm声明在这方面似乎没有提供任何内容,手册甚至警告不要改变变量值(p132).未实现用于声明对变量的影响的GNU扩展(p115).

我也没有找到任何记忆障碍的内在因素(比如__memory_changed()Keil的armcc).

搜索网络或TI论坛也一无所获.

任何其他提示如何进行?

c concurrency memory-barriers ti-dsp texas-instruments

2
推荐指数
1
解决办法
831
查看次数

为什么即使使用Monitor,所有成员变量也不需要易变的线程安全性?(为什么模型真的有效?)

(我知道他们没有,但我正在寻找其实际工作的根本原因,而不使用volatile,因为没有什么可以防止编译器将变量存储在没有volatile的寄存器中......或者是......)

这个问题源于思想的不和谐,即没有易失性的编译器(理论上可以通过各种方式优化任何变量,包括将其存储在CPU寄存器中.)虽然文档说在使用锁定变量等同步时不需要.但实际上在某些情况下,编译器/ jit似乎无法知道您是否会在代码路径中使用它们.所以怀疑是在这里真正发生的事情,使记忆模型"工作".

在这个例子中,什么阻止编译器/ jit优化_count到一个寄存器,从而在寄存器上完成增量而不是直接到存储器(稍后在退出调用后写入内存)?如果_count是易变的,那么看起来一切都应该没问题,但很多代码都是在没有volatile的情况下编写的.如果在方法中看到锁定或同步对象,编译器可能知道不会将_count优化到寄存器中,但在这种情况下,锁定调用是在另一个函数中.

大多数文档都说如果使用锁等同步调用,则不需要使用volatile.

那么是什么阻止了编译器优化_count到寄存器并可能只更新锁中的寄存器?我有一种感觉,大多数成员变量都不会因为这个原因被优化到寄存器中,因为每个成员变量都需要是易变的,除非编译器告诉它不应该优化(否则我怀疑大量的代码会失败) .我看到类似的东西,看看C++多年前本地函数变量存储在寄存器中,类成员变量没有.

所以主要的问题是,它是否真的是没有volatile的唯一方法,编译器/ jit不会将类成员变量放在寄存器中,因此不需要volatile?

(请忽略调用中缺少异常处理和安全性,但你得到了要点.)

public class MyClass
{
  object _o=new object();

  int _count=0;

  public void Increment()
  {
    Enter();
    // ... many usages of count here...
    count++;
    Exit();
  }




//lets pretend these functions are too big to inline and even call other methods 
// that actually make the monitor call (for example a base class that implemented these) 
  private void Enter() { Monitor.Enter(_o); }  
  private void Exit()  { Monitor.Exit(_o); }  //lets pretend this function …
Run Code Online (Sandbox Code Playgroud)

c# multithreading volatile memory-barriers

2
推荐指数
1
解决办法
291
查看次数

x86 CPU有多少个内存屏障指令?

我已经发现,在x86 CPU有以下内存屏障指令:mfencelfence,和sfence

x86 CPU是否仅具有这三个内存屏障指令,或者还有更多指令?

x86 assembly multithreading memory-barriers

2
推荐指数
2
解决办法
728
查看次数

如果非读取和非写入指令在x86中重新排序,这是否重要?

mfence 文件说以下内容:

对MFENCE指令之前发出的所有内存加载和存储到内存指令执行序列化操作.此序列化操作保证在遵循MFENCE指令的任何加载或存储指令之前,按程序顺序在MFENCE指令之前的每个加载和存储指令都变为全局可见.

据我所知,x86中没有fence指令可以防止非读取和非写入指令的重新排序.

现在如果我的程序只有一个线程,即使指令被重新排序,它仍然看起来好像指令正在按顺序执行.

但是,如果我的程序有多个线程,并且在其中一个线程中非读取和非写入指令被重新排序,其他线程会注意到这个重新排序(我假设答案是否定,否则会有一个fence指令停止非读取和非写入指令重新排序,或者我可能缺少某些东西)?

x86 assembly multithreading memory-barriers

2
推荐指数
1
解决办法
79
查看次数