Fork-join 中的内存可见性

发布于 2024-10-14 10:45:30 字数 404 浏览 3 评论 0原文

Brian Goetz 在 http://www.ibm.com/developerworks 上写了一篇关于 fork-join 的好文章/java/library/j-jtp03048.html。在其中,他列出了一种使用 fork-join 机制的合并排序算法,在该算法中,他并行地在数组的两侧执行排序,然后合并结果。

该算法同时对同一数组的两个不同部分进行排序。为什么不需要 AtomicIntegerArray 或其他一些机制来维持可见性?有什么保证一个线程会看到另一个线程完成的写入,或者这是一个微妙的错误?作为后续,Scala 的 ForkJoinScheduler 也做出这样的保证吗?

谢谢!

Brian Goetz's wrote a nice article on fork-join at http://www.ibm.com/developerworks/java/library/j-jtp03048.html. In it, he lists a merge sort algorithm using the fork-join mechanism, in which he performs the sort on two sides of an array in parallel, then merges the result.

The algorithm sorts on two different sections of the same array simultaneously. Why isn't an AtomicIntegerArray or some other mechanism necessary to maintain visibility? What guarantee is there that one thread will see the writes done by the other, or is this a subtly bug? As a follow up, does Scala's ForkJoinScheduler also make this guarantee?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

聚集的泪 2024-10-21 10:45:30

连接(ForkJoin)本身需要一个同步点,这是最重要的信息。同步点将确保在该点之后发生的所有写入都是可见的。

如果您查看代码,您可以看到同步点发生的位置。这只是一个方法调用 invokeAll

public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
    t2.fork();
    t1.invoke();
    t2.join();
}

这里 t2 分叉到另一个进程,t1 执行它的任务并且调用线程将等待 t2.join()。当经过t2时。所有对 t1 和 t2 的写入都将可见。

编辑:此编辑只是为了对同步点的含义进行更多解释。

假设您有两个变量,

int x;
volatile int y;

任何时候您写入 y 时,在读取 y 之前发生的所有写入都将可用。例如

public void doWork(){
   x = 10;
   y = 5;
}

,如果另一个线程读取 y = 5,则该线程保证读取 x = 10。这是因为对 y 的写入创建了一个同步点,其中之前的所有写入都在该同步点中。写入后点将可见。

使用 Fork Join 池,ForkJoinTask 的加入将创建一个同步点。现在,如果 t2.fork() 和 t1.invoke() t2 的加入将确保之前发生的所有写入都将被看到。由于所有先前的写入都在同一结构内,因此可见性是安全的。

如果还不清楚,我很乐意进一步解释。

The join (of ForkJoin) itself requires a synchronization point, thats the most important piece of information. A synchronization point will ensure that all writes that happen are visible after said point.

If you take a look at the code you can see where the synchronization point occurs. This is just one method call invokeAll

public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
    t2.fork();
    t1.invoke();
    t2.join();
}

Here t2 forks into another process, t1 executes its task and that calling thread will wait on t2.join(). When passing t2. All writes to t1 and t2 will then be visible.

Edit: This edit is just to give a little more of an explanation of what I meant by synchronization point.

Lets say that you have two variables

int x;
volatile int y;

Any time you write to y all writes that happened before you read y will be available. For example

public void doWork(){
   x = 10;
   y = 5;
}

If another thread reads y = 5 that thread is guaranteed to read x = 10. This is because the write to y creates a synchronization point in which all writes before said point will be visible after the write.

With the Fork Join pool the join of a ForkJoinTask will create a synchronization point. Now if t2.fork() and t1.invoke() the joining of t2 will ensure that all writes that previously happened will be seen. Since all the previous writes are within the same structure it will be safe for visibility.

I would be happy to explain further if that isnt as clear.

半岛未凉 2024-10-21 10:45:30

只是猜测:合并包括在线程上加入,并且加入保证了可见性。

第二部分是肯定的;我不知道合并是如何实现的。

Just a guess: merge includes joining on a Thread, and the join guarantees the visibility.

The second part is sure; I don't know how is merge implemented.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文