For循环等待所有CompletableFuture返回结果,并将结果添加到整数中。

huangapple go评论72阅读模式
英文:

For loop wait for all CompletableFutures to give result, and add result to int

问题

我有一个方法getCount(id)它从数据库中返回一个整数类型为CompletableFuture<Integer>我想遍历所有的id获取它们的结果并将这些结果累加到一个全局整数中然后我想对这个整数做一些操作

```java
int totalCount;
for (String id : API.getIDs()) {
    // OtherAPI.getCount(id)将返回CompletableFuture<Integer>
    OtherAPI.getCount(id).thenAccept(count -> totalCount += count);
}

System.out.println("" + totalCount);

这会导致错误,因为在thenAccept中不能将一个整数添加到另一个整数,因为它是一个消费者。有什么方法可以解决这个问题?


<details>
<summary>英文:</summary>

I have a method getCount(id) that returns an integer from a database, as a CompletableFuture&lt;Integer&gt;. I want to loop through all the ids, and get the result for all of them, and add them to one global int. Then I want to do something with that int:

```java
int totalCount;
for (String id : API.getIDs()) {
    //OtherAPI.getCount(id) will return the CompletableFuture&lt;Integer&gt;
    OtherAPI.getCount(id).thenAccept(count -&gt; totalCount += count);
}

System.out.println(&quot;&quot; + totalCount);

This gives an error, because you can't add an integer to another integer in thenAccept, because it's a consumer. What is the way to do this?

答案1

得分: 2

请注意,传递给 thenAccept 的函数可以由任意线程执行,所以 Java 不允许它们修改局部变量,这是一件 幸运的 事情。当您将变量更改为堆上的字段时,编译器会接受它,但结果会完全破坏。

最简单的解决方案是

int totalCount = 0;
for(String id: API.getIDs()) {
    totalCount += OtherAPI.getCount(id).join();
}

它只是等待每个值的可用性,然后在本地对它们求和。根据 OtherAPI.getCount(…) 调用封装的操作,这甚至可能是最有效的解决方案。

但是,当这些操作需要显着时间并且确实可以真正并行运行,即在内部不依赖于共享资源时,等待所有操作开始之前可能是有益的。

您可以这样做

CompletableFuture<Integer> result = CompletableFuture.completedFuture(0);
for(String id: API.getIDs()) {
    result = result.thenCombine(OtherAPI.getCount(id), Integer::sum);
}
System.out.println(result.join());

在这里,循环完全无需等待。它将安排求和操作,在两个操作完成时执行。然后,只有最后的 join 调用会等待最终结果。此时,所有异步操作已经被提交。

英文:

Mind that the function passed to thenAccept can be performed by an arbitrary thread, so thankfully, Java does not allow them to modify a local variable. When you change the variable to a field on the heap, the compiler would accept it, but the result would be entirely broken.

The simplest solution would be

int totalCount = 0;
for(String id: API.getIDs()) {
    totalCount += OtherAPI.getCount(id).join();
}

It just waits for the availability of each value, to sum them locally. Depending on the operations encapsulated by the OtherAPI.getCount(…) calls, this might even be the most efficient solution.

But when these operations take a significant time and can truly run in parallel, i.e. do not depend on a shared resource internally, it might be beneficial not to wait before all operations have been commenced.

You can do this like

CompletableFuture&lt;Integer&gt; result = CompletableFuture.completedFuture(0);
for(String id: API.getIDs()) {
    result = result.thenCombine(OtherAPI.getCount(id), Integer::sum);
}
System.out.println(result.join());

Here, the loop is entirely wait-free. It will schedule the summing operation, to be done when two operations have been completed. Then, only the join call at the end will wait for the final result. At this point, all asynchronous operations have been submitted already.

huangapple
  • 本文由 发表于 2020年9月13日 22:08:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/63871721.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定