1

以下构造是否是线程安全的,假设 foo 的元素已正确对齐和调整大小以便没有单词撕裂?如果不是,为什么不呢?

注意:下面的代码是我想做的一个玩具示例,而不是我实际的现实世界场景。显然,在我的示例中,有更好的编码可观察行为的方法。

uint[] foo;
// Fill foo with data.

// In thread one:
for(uint i = 0; i < foo.length; i++) {
     if(foo[i] < SOME_NUMBER) {
         foo[i] = MAGIC_VAL;
     }
}

// In thread two:
for(uint i = 0; i < foo.length; i++) {
     if(foo[i] < SOME_OTHER_NUMBER) {
         foo[i] = MAGIC_VAL;
     }
}

乍一看,这显然不安全,所以我将强调为什么我认为它可能是安全的:

  1. 仅有的两个选项是 foo 的元素保持不变或设置为 MAGIC_VAL。
  2. 如果线程二在更新时看到 foo[i] 处于中间状态,那么只会发生两件事:中间状态是 <SOME_OTHER_NUMBER或者不是。如果是 < SOME_OTHER_NUMBER,线程二也会尝试将其设置为 MAGIC_VAL。如果没有,线程二将什么也不做。

编辑:另外,如果 foo 是 long 或 double 什么的,那么更新它就不能自动完成?您仍然可以假设对齐等是这样的,即更新 foo 的一个元素不会影响任何其他元素。此外,在这种情况下,多线程的重点是性能,因此任何类型的锁定都会破坏这一点。

4

5 回答 5

4

在现代多核处理器上,您的代码在没有内存屏障的情况下不是线程安全的(至少在大多数语言中)。简而言之,如果没有明确的障碍,每个线程都可以从缓存中看到 foo 的完全不同的副本。

假设您的两个线程在某个时间点运行,然后在稍后某个时间点第三个线程读取 foo,它可能会看到一个完全未初始化的 foo,或者其他两个线程中的任何一个的 foo,或者混合两者都取决于 CPU 内存缓存的情况。

我的建议 - 不要试图在并发方面“聪明”,始终尝试“安全”。Smart每次都会咬你。破碎的双重检查锁定文章对在没有内存障碍的情况下内存访问和指令重新排序可能发生的情况有一些令人大开眼界的见解(尽管特别是关于 Java 及其(不断变化的)内存模型,它对任何语言都很有见地)。

您必须真正掌握语言指定的内存模型才能使用快捷方式。例如,Java 允许将变量标记为 volatile,它与记录为具有原子分配的类型相结合,可以通过强制它们进入主内存来允许非同步分配和获取(因此线程不会观察/更新缓存副本) .

于 2009-01-15T20:35:06.457 回答
1

您可以通过比较和交换操作安全且无锁定地执行此操作。您所拥有的看起来是线程安全的,但编译器可能会在某些情况下创建未更改值的写回,这将导致一个线程踩到另一个线程。

此外,这样做可能不会获得您想象的那么高的性能,因为像这样让两个线程写入相同的连续内存会导致CPU 缓存内的MESI 转换风暴,每个都非常慢。有关多线程内存一致性的更多详细信息,您可以查看 Ulrich Drepper 的“ What Every Programmer Should Know About Memory ”的第 3.3.4 节。

于 2009-01-15T20:34:19.657 回答
0

如果对每个数组元素的读写都是原子的(即它们正确对齐,没有你提到的单词撕裂),那么这段代码应该没有任何问题。如果foo[i]小于SOME_NUMBER或中的任何SOME_OTHER_NUMBER一个,则至少一个线程(可能两者)将MAGIC_VAL在某个点将其设置为;否则,它将保持不变。使用原子读写,没有其他可能性。

但是,由于您的情况更复杂,所以要非常小心——确保foo[i]每个循环只读取一次并存储在局部变量中。如果您在同一迭代中多次阅读它,您可能会得到不一致的结果。即使您对代码进行最轻微的更改也可能立即使其在竞争条件下变得不安全,因此请对带有大红色警告标志的代码进行大量评论。

于 2009-01-15T20:01:08.710 回答
0

这是不好的做法,无论后果如何,您都不应该处于两个线程同时访问同一个变量的状态。您给出的示例过于简化,任何大多数复杂的示例几乎总是会出现与之相关的问题.. ...

记住:信号量是你的朋友!

于 2009-01-15T20:02:14.293 回答
0

该特定示例是线程安全的。

这里没有真正涉及中间状态。该特定程序不会混淆。

不过,我建议在阵列上使用互斥锁。

于 2009-01-15T20:04:16.190 回答