标签: memory-model

Delphi是否有任何与C的volatile变量等价的东西?

在C和C++中,变量可以标记为volatile,这意味着编译器不会对其进行优化,因为它可以在声明对象外部进行修改.在Delphi编程中是否有相同的东西?如果不是关键字,也许可以解决?

我的想法是使用绝对,但我不确定,这可能会引入其他副作用.

c delphi multithreading volatile memory-model

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

为什么(或不是)在构造函数中设置字段是否安全?

假设你有一个像这样的简单类:

class MyClass
{
    private readonly int a;
    private int b;

    public MyClass(int a, int b) { this.a = a; this.b = b; }

    public int A { get { return a; } }
    public int B { get { return b; } }
}
Run Code Online (Sandbox Code Playgroud)

我可以以多线程方式使用这个类:

MyClass value = null;
Task.Run(() => {
    while (true) { value = new MyClass(1, 1); Thread.Sleep(10); }
});
while (true)
{
    MyClass result = value;
    if (result != null && (result.A != 1 || …
Run Code Online (Sandbox Code Playgroud)

c# multithreading volatile memory-model

13
推荐指数
1
解决办法
951
查看次数

Java内存模型:创建最终实例字段的循环参考图是否安全,所有这些都在同一个线程中分配?

能比我更了解Java内存模型的人确认我理解以下代码是否正确同步?

class Foo {
    private final Bar bar;

    Foo() {
        this.bar = new Bar(this);
    }
}

class Bar {
    private final Foo foo;

    Bar(Foo foo) {
        this.foo = foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道这段代码是正确的,但我没有完成整个过程- 在数学之前.我确实找到了两个非正式的引文,表明这是合法的,尽管我有点担心完全依赖它们:

最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段; 并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.[ Java®语言规范:Java SE 7 Edition,第17.5节 ]

另一个参考:

对象的正确构造意味着什么?它只是意味着在构造过程中不允许对正在构造的对象的引用"逃逸".(有关示例,请参阅安全构造技术.)换句话说,不要在另一个线程可能看到它的任何地方放置对正在构造的对象的引用; 不要将它分配给静态字段,不要将其注册为任何其他对象的侦听器,依此类推.这些任务应在构造函数完成后完成,而不是在构造函数中完成.[ JSR 133(Java内存模型)常见问题解答,"最终字段如何在新JMM下工作?" ]

java concurrency multithreading memory-model java-memory-model

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

C#变量新鲜

假设我在一个类中有一个成员变量(具有原子读/写数据类型):

bool m_Done = false;
Run Code Online (Sandbox Code Playgroud)

后来我创建了一个将其设置为true的任务:

Task.Run(() => m_Done = true);
Run Code Online (Sandbox Code Playgroud)

我不在乎什么时候m_Done将被设置为true.我的问题是,我是否有C#语言规范和任务并行库的保证,如果我从不同的线程访问它,最终m_Done将成立?
例:

if(m_Done) { // Do something }
Run Code Online (Sandbox Code Playgroud)

我知道使用锁会引入必要的内存障碍,m_Done将在以后显示为true.此外,我可以在设置变量时使用Volatile.Write,在读取时使用Volatile.Read.我看到很多代码以这种方式编写(没有锁或易失性),我不确定它是否正确.

请注意,我的问题不是针对C#或.Net的特定实现,而是针对规范.如果在x86,x64,Itanium或ARM上运行,我需要知道当前代码的行为是否相似.

c# multithreading volatile memory-model task-parallel-library

13
推荐指数
1
解决办法
732
查看次数

事件和多线程再一次

我担心看似标准的前C#6模式的正确性可以解雇事件:

EventHandler localCopy = SomeEvent;
if (localCopy != null)
    localCopy(this, args);
Run Code Online (Sandbox Code Playgroud)

我已经阅读了Eric Lippert的事件和比赛,并且知道调用过时的事件处理程序还有一个问题,但我担心的是,是否允许编译器/ JITter优化掉本地副本,有效地将代码重写为

if (SomeEvent != null)
    SomeEvent(this, args);
Run Code Online (Sandbox Code Playgroud)

有可能NullReferenceException.

根据C#语言规范,§3.10,

必须保留这些副作用的顺序的关键执行点是对volatile字段(第10.5.3节),锁语句(第8.12节)以及线程创建和终止的引用.

- 所以在上述模式中没有关键执行点,优化器也不受此限制.

Jon Skeet(2009年)的相关答案指出

由于条件的原因,JIT不允许在第一部分中执行您正在讨论的优化.我知道这是作为一个幽灵提出的,但它无效.(我刚才和Joe Duffy或Vance Morrison一起检查过;我不记得是哪一个.)

- 但是评论引用了这篇博客文章(2008年):事件和线程(第4部分),它基本上说CLR 2.0的JITter(可能是后续版本?)不能引入读取或写入,所以一定没有问题在Microsoft .NET下.但这似乎与其他.NET实现没有任何关系.

[旁注:我没有看到不引入读取证明了所述模式的正确性.难道JITter只是看到一些SomeEvent其他局部变量的陈旧值并优化其中一个读取,而不是另一个?完全合法,对吗?]

此外,这篇MSDN文章(2012年):Igor Ostrovsky的理论与实践中的C#记忆模型陈述如下:

非重新排序优化某些编译器优化可能会引入或消除某些内存操作.例如,编译器可能用一次读取替换字段的重复读取.类似地,如果代码读取字段并将值存储在局部变量中然后重复读取变量,则编译器可以选择重复读取该字段.

因为ECMA C#规范不排除非重新排序的优化,所以它们可能是允许的.实际上,正如我将在第2部分中讨论的那样,JIT编译器确实执行了这些类型的优化.

这似乎与Jon Skeet的答案相矛盾.

由于现在C#不再是Windows语言,因此问题在于,模式的有效性是否是当前CLR实现中有限的JITter优化的结果,或者是语言的预期属性.

所以,问题是:从C#-the-language的角度来看,正在讨论的模式是否有效?(这意味着是否需要语言编译器/运行时来禁止某种优化.)

当然,欢迎就该主题提出规范性参考.

.net c# multithreading memory-model language-lawyer

13
推荐指数
1
解决办法
362
查看次数

C++标准:可以将轻松的原子商店提升到互斥锁之上吗?

标准中是否有任何措辞可以保证放松存储到原子的位置不会超过锁定互斥锁?如果没有,是否有任何措辞明确表示编译器或CPU是否可以这样做?

例如,采取以下计划:

std::mutex mu;
int foo = 0;  // Guarded by mu
std::atomic<bool> foo_has_been_set{false};

void SetFoo() {
  mu.lock();
  foo = 1;
  foo_has_been_set.store(true, std::memory_order_relaxed);
  mu.unlock();
}

void CheckFoo() {
  if (foo_has_been_set.load(std::memory_order_relaxed)) {
    mu.lock();
    assert(foo == 1);
    mu.unlock();
  }
}
Run Code Online (Sandbox Code Playgroud)

CheckFoo如果另一个线程同时调用SetFoo,是否有可能在上述程序中崩溃,或者是否有一些保证不能将存储foo_has_been_set提升到mu.lock编译器和CPU 的调用之上?

这与一个较旧的问题有关,但我并不是100%明白,答案适用于此.特别是,该问题的答案中的反例可能适用于两个并发调用SetFoo,但我对编译器知道有一个调用SetFoo和一个调用的情况感兴趣CheckFoo.这是否保证安全?

我正在寻找标准中的具体引用.谢谢!

c++ atomic memory-model

13
推荐指数
1
解决办法
743
查看次数

C++ 0x中的Fences一般只保证原子或内存

的C++ 0x草案有围栏的概念似乎很明显,从围墙的CPU /芯片级的概念,或者说什么linux内核的家伙想到的围栏.问题在于草案是否真的意味着一个极其受限制的模型,或者措辞是否很差,它实际上意味着真正的围栏.

例如,在29.8 Fences下它表示如下:

如果存在原子操作X和Y,则释放围栏A与获取围栏B同步,两者都在某个原子对象M上操作,使得A在X之前被排序,X修改M,Y在B之前被排序,并且Y读取该值如果是释放操作,则由X写入或由假设释放序列中的任何一方写入的值X将结束.

它使用这些术语atomic operationsatomic object.草案中定义了这样的原子操作和方法,但它仅仅意味着那些吗?一个释放栅栏听起来像一个店围栏.在围栏之前不保证写入所有数据商店围栏几乎是无用的.类似于装载(获取)围栏和完整围栏.

那么,C++ 0x中的栅栏/栅栏是否适当的栅栏和措辞是否非常差,或者它们是否如所描述的那样极其受限制/无用?


就C++而言,假设我有这个现有的代码(假设现在可以使用围栏作为高级构造 - 而不是在GCC中使用__sync_synchronize):

Thread A:
b = 9;
store_fence();
a = 5;

Thread B:
if( a == 5 )
{
  load_fence();
  c = b;
}
Run Code Online (Sandbox Code Playgroud)

假设a,b,c的大小在平台上具有原子拷贝.以上意味着c只会被分配9.注意我们并不关心线程B何时看到a==5,只是当它看到它时b==9.

C++ 0x中保证相同关系的代码是什么?


答案:如果您阅读我选择的答案和所有评论,您将获得情况的要点.C++ 0x似乎强制您使用带栅栏的原子,而普通硬件栅栏没有此要求.在许多情况下,只要sizeof(atomic<T>) == sizeof(T)和,这仍然可以用来代替并发算法atomic<T>.is_lock_free() == true.

不幸的是,这is_lock_free …

c++ multithreading memory-model memory-barriers c++11

12
推荐指数
1
解决办法
2296
查看次数

C++内存模型 - 此示例是否包含数据竞争?

我正在阅读Bjarne Stroustrup的C++ 11 FAQ,我无法理解内存模型部分中的示例.

他给出了以下代码片段:

// start with x==0 and y==0
if (x) y = 1; // thread 1
if (y) x = 1; // thread 2
Run Code Online (Sandbox Code Playgroud)

FAQ说这里没有数据竞争.我不明白.内存位置x由线程1读取并由线程2写入而没有任何同步(并且同样适用y).这是两次访问,其中一次是写入.这不是数据竞争的定义吗?

此外,它说"每个当前的C++编译器(我所知道的)给出了正确答案." 这个正确答案是什么?根据一个线程的比较是在另一个线程的写入之前或之后发生(或者另一个线程的写入是否对读取线程可见),答案是否会有所不同?

c++ memory-model c++11

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

"存储缓冲区转发"在英特尔开发人员手册中的含义是什么?

英特尔64和IA-32架构软件开发人员手册说,大约由单一处理器的行动("在P6更多最近的处理器系列内存排序和"第8.2.2节)重新排序如下:

读取可以使用较旧的写入到不同位置进行重新排序,但不能使用较旧的写入到同一位置.

接下来讨论与早期处理器相比放松的点时,它说:

存储缓冲区转发,当读取将写入传递到同一存储器位置时.

据我所知,"存储缓冲区转发"并未在任何地方精确定义(也不是"通过").读取将写入传递到同一位置是什么意思,因为上面说它不能通过写入同一位置来重新排序?

concurrency assembly intel cpu-architecture memory-model

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

memory_order_relaxed如何在智能指针中增加原子引用计数?

请考虑以下代码片段摘自Herb Sutter关于原子的讨论:

smart_ptr类包含一个名为control_block_ptr的pimpl对象,其中包含引用计数refs.

// Thread A:
// smart_ptr copy ctor
smart_ptr(const smart_ptr& other) {
  ...
  control_block_ptr = other->control_block_ptr;
  control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
  ...
}

// Thread D:
// smart_ptr destructor
~smart_ptr() {
  if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 0) {
    delete control_block_ptr;
  }
}
Run Code Online (Sandbox Code Playgroud)

Herb Sutter说,线程A 中引用的增量可以使用memory_order_relaxed,因为"没有人根据动作做任何事情".现在我理解memory_order_relaxed,如果refs在某个时刻等于N ,则两个线程A和B执行以下代码:

control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
Run Code Online (Sandbox Code Playgroud)

然后可能会发生两个线程都认为refs的值为N并且都将N + 1写回它.这显然不起作用,memory_order_acq_rel应该像析构函数一样使用.我哪里错了?

编辑1:考虑以下代码.

atomic_int refs = N; // at time t0. 

// [Thread 1]
refs.fetch_add(1, memory_order_relaxed); // at time t1. 

// [Thread 2]
n = …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading memory-model c++11 relaxed-atomics

12
推荐指数
1
解决办法
3547
查看次数