是否存在用于Java中互斥的Peterson算法的示例实现?
考虑以下情况
// Global
int x = 0; // not atomic
// Thread 1
x = 1;
// Thread 2
if (false)
x = 2;
Run Code Online (Sandbox Code Playgroud)
根据标准,这是否构成数据竞争?[intro.races] 说:
如果两个表达式求值之一修改内存位置 (4.4),而另一个表达式求值读取或修改同一内存位置,则两个表达式求值会发生冲突。
如果程序的执行包含两个潜在并发冲突的操作,并且至少其中一个操作不是原子操作,并且两者都发生在另一个操作之前,则该程序的执行将包含数据争用,除了下面描述的信号处理程序的特殊情况之外。任何此类数据竞争都会导致未定义的行为。
从语言律师的角度来看是否安全,因为程序永远不能执行“表达式求值” x = 2;?
从技术角度来看,如果某个奇怪、愚蠢的编译器决定对该写入执行推测执行,并在检查实际情况后将其回滚,该怎么办?
激发这个问题的原因是(至少在标准 11 中),允许以下程序的结果完全取决于重新排序/推测执行:
// Thread 1:
r1 = y.load(std::memory_order_relaxed);
if (r1 == 42) x.store(r1, std::memory_order_relaxed);
// Thread 2:
r2 = x.load(std::memory_order_relaxed);
if (r2 == 42) y.store(42, std::memory_order_relaxed);
// This is allowed to result in r1==r2==42 in c++11
Run Code Online (Sandbox Code Playgroud)
有一些东西让我对Java内存模型感到困扰(如果我甚至正确理解了所有内容).如果有两个线程A和B,则无法保证B将看到A写入的值,除非A和B在同一监视器上同步.
对于保证线程之间的高速缓存一致性的任何系统体系结构,没有问题.但是,如果架构不支持硬件中的缓存一致性,这实质上意味着每当线程进入监视器时,之前所做的所有内存更改都必须提交到主内存,并且缓存必须无效.它需要是整个数据缓存,而不仅仅是几行,因为监视器没有信息,它在内存中保存哪些变量.但这肯定会影响任何需要频繁同步的应用程序的性能(特别是像短时间运行的作业队列这样的事情).那么Java可以在没有硬件缓存一致性的架构上合理地工作吗?如果没有,为什么内存模型不能提供更强的可见性保证?如果语言需要监视器保护的信息,那会不会更有效率?
正如我所看到的,内存模型给我们带来了两个世界中最糟糕的,绝对需要同步,即使硬件中保证了高速缓存一致性,另一方面,非相干体系结构上的性能不佳(完全高速缓存刷新).因此,它不应该更严格(需要信息由监视器保护)或更多地丢失和限制缓存一致的架构的潜在平台?
就像现在一样,它对我来说没有多大意义.有人可以清楚为什么选择这种特定的记忆模型吗?
编辑:回想起来,我对严格和失败的使用是一个糟糕的选择.我对"保证较少"的情况使用"严格",而对相反则"丢失".为了避免混淆,最好用更强或更弱的保证来说话.
std::atomic功能,诸如store和load采取一个std::memory_order参数.可以在运行时确定参数,就像任何其他函数参数一样.但是,实际值可能会影响编译期间代码的优化.考虑以下:
std::atomic<int> ai1, ai2;
int value = whatever;
void foo() {
std::memory_order memOrd = getMemoryOrder();
register int v = value; // load value from memory
ai1.store(v, memOrd); // dependency on v's value
ai2.store(1, memOrd); // no dependency. could this be move up?
}
Run Code Online (Sandbox Code Playgroud)
如果memOrd碰巧memory_order_relaxed,第二个商店可以安全地移动到第一个商店前面.这将在加载value和使用它之间增加一些额外的工作,这可能会阻止其他需要的停顿.但是,如果memOrd是memory_order_seq_cst,则不应该允许切换存储,因为如果设置为1 ai1,value则某些其他线程可能指望已经设置为ai2.
我想知道为什么内存顺序被定义为运行时参数而不是编译时间.在决定最佳内存操作语义之前,有人在运行时检查环境是否有任何理由?
假设您有以下定义:
struct X
{
char a, b;
};
X x;
Run Code Online (Sandbox Code Playgroud)
现在假设您有两个线程,其中一个读取和写入x.a但从不访问,x.b而另一个读取和写入x.b但从不访问x.a.两个线程都不使用任何锁或其他同步原语.这可以保证在C++ 11中有效吗?或者它是否算作访问同一个对象,因此需要锁定?
我已经看到这个关于获取,释放,消费等的问题存在,但是,没有答案真正定义了"消费操作"实际上是什么.
在1.10第5段中,它指出:
在一个或多个存储器位置上的同步操作是消费操作,获取操作,释放操作,或获取和释放操作两者.
我想知道是否有人可以解释这是什么,因为它在C++ 11标准的1.10部分中使用?
在C#中,这是以线程安全方式调用事件的标准代码:
var handler = SomethingHappened;
if(handler != null)
handler(this, e);
Run Code Online (Sandbox Code Playgroud)
其中,可能在另一个线程上,编译器生成的add方法用于Delegate.Combine创建新的多播委托实例,然后在编译器生成的字段上设置该实例(使用互锁比较交换).
(注意:出于这个问题的目的,我们不关心在事件订阅者中运行的代码.假设它在删除时是线程安全且健壮的.)
在我自己的代码中,我想按照以下方式做类似的事情:
var localFoo = this.memberFoo;
if(localFoo != null)
localFoo.Bar(localFoo.baz);
Run Code Online (Sandbox Code Playgroud)
哪里this.memberFoo可以由另一个线程设置.(这只是一个线程,所以我不认为它需要联锁 - 但也许这里有副作用?)
(并且,显然,假设它Foo是"不可变的",我们在这个线程上使用它时不会主动修改它.)
现在我理解这是线程安全的明显原因:从引用字段读取是原子的.复制到本地可确保我们不会获得两个不同的值.(显然只能从.NET 2.0保证,但我认为它在任何理智的.NET实现中都是安全的吗?)
但我不明白的是:被引用的对象实例所占用的内存如何?特别是在缓存一致性方面?如果"writer"线程在一个CPU上执行此操作:
thing.memberFoo = new Foo(1234);
Run Code Online (Sandbox Code Playgroud)
什么保证Foo分配新内存的内存不会出现在"读取器"运行的CPU的缓存中,具有未初始化的值?什么确保localFoo.baz(上面)不读取垃圾?(跨平台的保证有多好?在Mono上?在ARM上?)
如果新创建的foo恰好来自游泳池呢?
thing.memberFoo = FooPool.Get().Reset(1234);
Run Code Online (Sandbox Code Playgroud)
从内存的角度来看,这似乎没有什么不同,只是一个新的分配 - 但是.NET分配器可能会让第一个案例有效吗?
在我提出这个问题时,我的想法是需要一个内存屏障来确保 - 不是因为读取依赖而不能移动内存访问 - 而是作为CPU的一个信号来清除任何缓存失效.
我的来源是维基百科,所以你要做的就是这样.
(我可能会猜测,也许在连动比较交换作家线程上无效缓存读者?或者,也许所有的读取原因失效吗?或者指针引用引起失效?我特别关注如何平台特有的这些东西的声音.)
更新:只是为了更明确地说明问题是关于CPU缓存失效以及.NET提供的保证(以及这些保证可能如何依赖于CPU架构):
Q(内存位置)中存储了引用.R …我有一个关于GCC-Wiki文章的问题.在标题"总结"下,给出了以下代码示例:
线程1:
y.store (20);
x.store (10);
Run Code Online (Sandbox Code Playgroud)
线程2:
if (x.load() == 10) {
assert (y.load() == 20)
y.store (10)
}
Run Code Online (Sandbox Code Playgroud)
据说,如果释放所有商店并获得所有负载,则线程2中的断言不会失败.这对我来说很清楚(因为线程1中x的存储与线程2中x的加载同步).
但现在出现了我不理解的部分.还有人说,如果所有商店都被释放并且消耗了所有负载,那么结果是相同的.来自y的负载是否可能在x的负载之前被提升(因为这些变量之间没有依赖关系)?这意味着线程2中的断言实际上可能会失败.
我们应该声明私有字段,就像volatile在多个线程中使用instanced一样吗?
在Effective Java中,有一个例子,代码在没有volatile的情况下不起作用:
import java.util.concurrent.TimeUnit;
// Broken! - How long would you expect this program to run?
public class StopThread {
private static boolean stopRequested; // works, if volatile is here
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested)
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
Run Code Online (Sandbox Code Playgroud)
解释说
while(!stopRequested)
i++;
Run Code Online (Sandbox Code Playgroud)
优化到这样的事情:
if(!stopRequested)
while(true)
i++;
Run Code Online (Sandbox Code Playgroud)
所以stopRequested后台线程看不到进一步的修改,所以它永远循环.(顺便说一句,该代码在没有 …
在x86架构上,存储到同一内存位置的总订单有,例如,请参阅此视频.C++ 11内存模型有哪些保证?
更确切地说,在
-- Initially --
std::atomic<int> x{0};
-- Thread 1 --
x.store(1, std::memory_order_release);
-- Thread 2 --
x.store(2, std::memory_order_release);
-- Thread 3 --
int r1 = x.load(std::memory_order_acquire);
int r2 = x.load(std::memory_order_acquire);
-- Thread 4 --
int r3 = x.load(std::memory_order_acquire);
int r4 = x.load(std::memory_order_acquire);
Run Code Online (Sandbox Code Playgroud)
结果r1==1, r2==2, r3==2, r4==1是否允许(在x86以外的某些架构上)?如果我要更换所有memory_order的东西std::memory_order_relaxed怎么办?