【问题标题】:Can a Stream be sequentially processed for part of the pipeline, and then as parallel?可以为流水线的一部分顺序处理流,然后并行处理吗?
【发布时间】:2013-12-17 16:57:21
【问题描述】:

我的以下代码无法按预期工作(跳过了随机行,而不是第一行):

Files.lines(path)
     .skip(1)
     .parallel()
     .forEach( System.out::println )

我感觉我误解了 Streams 的行为。问题是:我可以先将流视为顺序流(并使用“有状态的中间操作”),然后将其输入并行forEach

【问题讨论】:

  • 你在做线性运算,为什么要用.parallel()
  • @Esko 显然,“println”是处理器密集型操作的替代品,我根据文件中的条目执行此操作。
  • sheitt [尴尬] 我的运行时没有并行执行它们......!
  • 原来我的 jUnit 测试没有并行执行(不知道为什么??)但非测试可以。完全把我弄糊涂了!无论如何,skip 的行为似乎是非常不可预测的,有时它是最后一个被跳过的项目。甚至 parallel() 也很不稳定......有时只有在迭代次数很高并且列表中的项目数很高时才会创建线程。对于慢速作业(需要大量计算),它没有创建任何线程。对于 1000 多个短作业(需要几毫秒的时间),它确实是并行执行的。测试看看。
  • 看Javadoc,我想你想要的是forEachOrdered。然后跳过将正常工作并且流将是并行的。但是,您仍然必须忍受一个非常不可预测和无法控制的线程生成公式。最好自己动手。除非,就像我尝试过的那样,你有许多短暂的任务,那么它似乎可以工作。

标签: java java-8 java-stream


【解决方案1】:

整个流水线要么是并行的,要么是顺序的。

尝试使用forEachOrdered 而不是forEach。在我的测试中,如果使用forEachOrdered,它会跳过第一行(forEach 它会跳过最后一行)。

forEach忽略遇到顺序,好像也可以让其他操作忽略它。

【讨论】:

  • 您有此信息的来源吗?也许有更多细节?
【解决方案2】:

这不是错误,而是一项功能。调用parallel() 使整个流并行。除非随后调用 sequential(),否则会将整个流设置回顺序模式。

The javaodoc 说:

返回一个等效的并行流。

【讨论】:

  • 如何在不将所有内容转储到中间集合的情况下实现所需的行为?
  • 我猜你可以使用StreamSupport.stream(Files.lines(path).skip(1).spliterator(), true)。虽然没有测试。
  • 就这么没礼貌?出于语义和性能原因,按顺序对流执行部分操作可能不是一个好主意吗?
  • 这也是非常令人惊讶的行为,后来的链接调用极大地改变了前面的语义。
  • @AleksandrDubinsky:实际上,将管道从并行切换到顺序并返回过去是可能的,但在流设计中相对较晚进行了更改。我在this answer 中谈到了它,还有一些由 Brian Goetz 本人编写的 cmets。如果您仍然想知道原因,您可能会找到详细说明更改的邮件列表。
【解决方案3】:

不,你不能那样做。但是,您的代码应该可能按预期工作,来自Stream.skip javadocs

虽然 skip() 在顺序流管道上通常是一种便宜的操作,但在有序并行管道上可能会非常昂贵,尤其是对于较大的 n 值,因为 skip(n) 不仅限于跳过任何 n 个元素,而且相遇顺序中的前 n 个元素。如果您的情况的语义允许,使用无序流源(例如 generate(Supplier))或使用 BaseStream.unordered() 删除排序约束可能会导致并行管道中的 skip() 显着加速。如果需要与遇到顺序保持一致,并且您在并行管道中使用 skip() 时遇到性能或内存利用率不佳的情况,则使用 BaseStream.sequential() 切换到顺序执行可能会提高性能。

您的代码是否有效取决于Files.lines(..) 返回的流的性质,这取决于该流是否为Ordered。这些特征由使用的 Spliterator 设置,如果流是有序的,那么它将始终跳过第一个元素。如果流是无序的,那么它将跳过一个元素。

http://download.java.net/jdk8/docs/api/java/util/Spliterator.html

【讨论】:

  • 我找不到检查流是否有序的方法(真的没有办法吗?),但在Files.lines 的源代码中我看到了Spliterator.ORDERED。另外,为什么有人会在无序流上调用skip?为什么不直接抛出异常?
  • 你仍然可以跳过第一个元素,即使元素没有自然排序。
  • 为什么我要跳过 random 元素?
  • 我想不出一个好的用例,但它比抛出异常要好。
  • 根据Java 哲学。
【解决方案4】:

skip(n) 似乎不会跳过并行流中的前 n 个元素。

解决方案,使用 BufferedReader readLine() 方法将前 [n] 行切掉。

然后获取Stream,它将继续与读者离开的地方:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.stream.IntStream;

public class TestStreams {

    public static void main(String[] args) throws Exception{
         unordered();
    }

    public static void unordered() throws IOException, InterruptedException {

        StringBuilder sb = new StringBuilder();
        IntStream.range(0, 1000).forEach(n -> sb.append(n).append("\n"));

        try (BufferedReader br = new BufferedReader(new StringReader(sb.toString()))) {
            if (br.readLine() != null) {
                br.lines()
                        .parallel()
                        .forEach(it -> System.out.println(Thread.currentThread() + " : " + it));
            }
        }
    }  
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
相关资源
最近更新 更多