【问题标题】:How to use print("\r"+progressMessage) in a Java parallelStream?如何在 Java 并行流中使用 print("\r"+progressMessage)?
【发布时间】:2017-07-18 10:15:53
【问题描述】:

我正在尝试通过编写类似的内容来显示我的并行流的进度

int total=myList.size();
AtomicInteger counter=new AtomicInteger(0);

List<String> myResult=IntStream.range(0, total)
                    .parallel()
                    .mapToObj(i-> modify(myList.get(i)))
                    .peek(i->System.out.print("\r"+(counter.getAndIncrement()*100/total)+"%"))
                    .collect(toList());

我的问题来自“\r”。鉴于它是并行化的,当并发事件发生时,真正到达行首所需的“\r”数量可能会有所不同。所以有时我可以读到“70% 71%”...

【问题讨论】:

  • 为什么要自己添加退货? println 有什么问题?
  • println 转到下一行。我想用新百分比替换旧百分比。在同一行。
  • 嗯?你不能那样做。
  • @Michael 为什么不呢,我的意思是使用\\r 跳转到当前行的开头?运行良好(在 Linux 上)。您肯定必须有一个终端来接受这一点,这与 Eclipse 控制台和可能的 Windows 控制台不同。
  • @CarlosHeuberger 好的,我刚刚查了一下。我不理解单独使用回车的确切语义。看起来它可能会在 Windows 上运行。

标签: java parallel-processing java-stream


【解决方案1】:

将进度记录与进度输出操作分开:

int total = myList.size();
AtomicInteger counter = new AtomicInteger(0);
ScheduledExecutorService es = Executors.newScheduledThreadPool(1);

ScheduledFuture<?> f = es.scheduleWithFixedDelay(new Runnable() {
    int lastReported = -1;
    public void run() {
        int newValue = counter.get();
        if(newValue != lastReported) {
            lastReported = newValue;
            System.out.append("\r"+newValue*100/total+"%").flush();
        }
    }
}, 100, 100, TimeUnit.MILLISECONDS);

List<String> myResult = IntStream.range(0, total)
    .parallel()
    .mapToObj(i -> modify(myList.get(i)))
    .peek(i -> counter.incrementAndGet())
    .collect(toList());

f.cancel(true);
es.shutdown();
System.out.print("\r100%");

现在,打印始终由一个线程完成。这种分离解决了更多的问题。 对每个元素执行打印操作会显着减慢您的实际处理速度。通过使用预定的打印作业,您可以自己控制开销,即您不需要比人类阅读速度更快的打印,并且如果自上次更新以来百分比没有变化,您也不需要执行昂贵的打印操作。

请注意,当所有元素都通过peek 操作时,流操作尚未完全完成(至少有一个合并操作待处理),但这是您可以使用当前 API 获得的最佳进度估计。

【讨论】:

  • 好吧,我希望程序足够简单,可以证明这不是程序的问题。考虑一下,您的原始代码的System.out.print 在内部使用了同步,因此它也不应该表现出这种行为,这表明问题出在其他地方。尽管如此,在您的原始代码中,这些操作可能会相互超越,从而导致打印的百分比不连续,并且还存在性能问题,因此您仍然应该采纳我的建议。但印刷问题必须在其他地方。如前所述,它可以在 Windows 控制台上顺利运行。
  • 它怎么会导致不连续的百分比,因为它是“计数器”会增加和打印而不是“i”? Aamof,我确实有单调的价值观(我想这就是你的意思)。我将尝试制作一个测试方法“modify()”,以将问题与我的其余代码隔离开来,我会尽快回复您。
  • @Myoch: counter 被自动查询和更新。 之后,您将使用int 值构造一个字符串,然后 将其传递给System.out.print。构造一个字符串需要一些时间,在此期间,稍后更新counter 的另一个线程可能会稍微更快地完成该字符串构造(因为线程调度是不可预测的)并打印其更高的数字。然后,第一个线程用较小的数字完成它的字符串构造并打印它,用较高的数字覆盖消息。
猜你喜欢
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 2022-10-22
  • 2016-07-23
  • 1970-01-01
  • 2016-02-03
  • 1970-01-01
  • 2018-06-21
相关资源
最近更新 更多