【问题标题】:Martin Odersky : Working hard to keep it simpleMartin Odersky:努力保持简单
【发布时间】:2014-05-04 15:53:56
【问题描述】:

我正在看Martin Odersky在coursera scala课程中他自己推荐的演讲,我对它的一个方面很好奇

var x = 0
async { x = x + 1 }
async { x = x * 2 }

所以我知道如果第一个语句先执行然后第二个语句,它可以给出 2:

x = 0;
x = x + 1;
x = x * 2; // this will be x = 2

我知道它是如何给出 1 的:

x = 0;
x = x * 2;
x = x + 1 // this will be x = 1

但是它怎么会导致 0 呢?该语句是否可能根本不执行?

对不起,这么简单的问题,但我真的坚持下去

【问题讨论】:

    标签: scala asynchronous logic


    【解决方案1】:

    您需要考虑交错 执行。请记住,CPU 需要读取x 的值才能对其进行处理。所以想象一下:

    1. 线程 1 读取 x(读取 0)
    2. 线程 2 读取 x(读取 0)
    3. 线程 1 写入 x + 1(写入 1)
    4. 线程 2 写入 x * 2(写入 0)

    【讨论】:

    • 交错执行将只允许线程执行。如果是第一个,则结果为 0。是这样吗?
    • 否 - 如果您有多个内核并且线程真正并行运行,情况也是如此。发生的事情是您的每个线程都在执行“读取修改写入”操作(读取 x 的值,然后写入新值)。而且这个读修改写是非原子的。
    • 所以进程不同步。线程 2 将 x 的值读取为 0,因为第一个线程没有完成将 1 的值分配给 x,因此结果将为 0。这次我明白了吗?
    • 是的,线程不同步,因此可以按任何顺序执行,包括交错的:-)
    【解决方案2】:

    我知道这已经被回答了,但也许这仍然有用:

    将其视为一系列原子操作。处理器一次执行一项原子操作。

    这里有以下内容:

    • 阅读 x
    • 写 x
    • 加1
    • 乘以 2

    以下两个序列保证在“自身内部”按此顺序发生:

    • 读 x,加 1,写 x
    • 读取 x,乘以 2,写入 x

    但是,如果您并行执行它们,则每个原子操作相对于另一个序列中的任何其他原子操作的执行时间是随机的,即这两个序列交错。

    其中一个可能的执行顺序将产生 0,这是 Paul Butcher 在回答中给出的

    这是我在网上找到的一个插图:

    每个蓝色/紫色块是一个原子操作,您可以看到如何根据块的顺序产生不同的结果

    要解决此问题,您可以使用关键字“同步

    我的理解是,如果您在同一个对象中将两个代码块(例如两个方法)标记为同步,那么每个块在执行时都将拥有该对象的锁,因此在第一个块时不能执行另一个块还没有完成。但是,如果您在两个不同的对象中有两个同步块,那么它们可以并行执行。

    【讨论】:

    • 这是一个非常详细和深入的答案。这真的很有帮助。 +1
    • 我很抱歉 - 也许我遗漏了一些东西,但我不知道 async 将它提供给单个原子操作的序列。确实,如果这样做了,那么答案 0 就不可能了,是不是因为不可能进行交错操作?
    • @PaulButcher pffft,对不起,我在想“同步”。我会修正我的答案
    • @Bula 请检查我所做的编辑,我的意思是说“同步”
    • 请原谅我的迂腐,但同步也不会使它给出的代码块成为原子的。它所做的是声明与对象关联的内在锁。所以它会让你与其他一些声称具有相同内在锁的代码相互排斥,但这与使其成为原子不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 1970-01-01
    • 2020-07-12
    • 2015-11-13
    • 2016-05-16
    • 1970-01-01
    相关资源
    最近更新 更多