简体   繁体   English

从内部完成ComputableFuture注入

[英]CompletableFuture injection from the inside

Is it possible to inject a CompletableFuture in a CompletableFuture chain from the inside ? 是否可以从内部CompletableFuture链中注入 CompletableFuture

I'm working with a function like this: 我正在使用这样的功能:

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // ... Some processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        if (!some condition satisfied) {
            // This is where I want the injection to happen. 
            // This stage should be suspended and a new stage should be injected between this point and the next stage.
        }

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

At the injection point if (!some condition satisfied) , I'd like to run a query that (say) takes 5 seconds to execute and retrieve some data needed by the final stage. 在注入点if (!some condition satisfied) ,我想运行一个查询(例如),该查询需要5秒钟执行并检索最后阶段所需的一些数据。 I don't want to block the thread for 5 seconds, eg make the query synchronous inside the if , I want it to run asynchronous, and when the results are back go straight to the next stage. 我不想阻塞线程5秒钟,例如,使查询在if同步,我希望它异步运行,然后返回结果直接进入下一阶段。 The problem I'm having is that the condition is known only inside the chain. 我遇到的问题是该条件仅是已知的。

Anyone have ideas on this? 有人对此有想法吗?


EDIT 编辑

I'll try to clarify the question. 我将尝试澄清这个问题。 I originally had one piece of code only. 我本来一条唯一的代码。 Now I'm trying to optimize the code so that I spawn a lower number of threads. 现在,我正在尝试优化代码,以便产生更少的线程。

The point is that at the injection point I want to issue something like (sorry, Datastax Java driver for Cassandra code snippet): 关键是在注入点我想发出类似的消息(对不起, Cassandra代码片段的Datastax Java驱动程序 ):

ResultSetFuture rsFuture = session.executeAsync(query);

and inject that future into the chain. 并注入未来进入连锁。 This would make the calling thread "free" of performing other things instead of sitting and waiting for the results. 这将使调用线程“无需”执行其他操作,而不必坐下来等待结果。


I don't know if I can make it more clear than this, but let's follow this example. 我不知道是否可以比这更清楚,但是让我们跟随这个例子。

I run a loop in the main thread: 我在主线程中运行一个循环:

for (int i = 0; i < 1000; i++) {
    getFutureOfMyLongRunningTask(i);
}

This loop lives on the main thread only, but each call to the function enqueues a new task in a thread pool P . 该循环仅存在于主线程上,但是对函数的每次调用都会在线程池P中 排队一个新任务。 Assume now that P is a fixed thread pool of size 1 . 现在假设P是大小为1的固定线程池。 That means only one thread exists in P , and it is able to process 1 task only. 这意味着P中仅存在一个线程,并且只能处理1个任务。 The main loop, however, will enqueue all 1000 tasks. 但是,主循环将使所有1000个任务入队。 The main loop then will need to wait for all tasks to finish. 然后,主循环将需要等待所有任务完成。

Now suppose that the 1st task out of 1000 needs to perform a long DB query. 现在假设1000个任务中的第一个任务需要执行一个长数据库查询。 We have now two choices: 现在,我们有两个选择:

  1. The query is executed sync inside the processing thread (belonging to the thread pool P ). 该查询在处理线程内部 (属于线程池P同步执行。 That means I simply issue the query inside the if (!some condition satisfied) block and wait for the results. 这意味着我只是在if (!some condition satisfied)块内发出查询,然后等待结果。 This effectively blocks the tasks processing because the thread pool P have no free threads. 这有效地阻止了任务处理,因为线程池P没有空闲线程。 The only one is busy blocked on IO. 唯一一个忙于 IO 阻塞

  2. The query is executed async inside the processing thread (belonging to the thread pool P ).That means I issue the query inside the if (!some condition satisfied) block and get immediately back a future that I will listen to (probably the DB driver will spawn another thread and block that thread waiting for the results). 该查询是在处理线程内部 异步执行的(属于线程池P ),这意味着我在if (!some condition satisfied)块内发出查询,并立即返回一个我将要收听的未来(可能是DB驱动程序)将产生另一个线程并阻止线程等待结果)。 However, the thread belonging to P now is free to process at least another task. 但是,属于P的线程现在可以自由处理至少另一个任务。

In my opinion, option 2 is better than option 1 , and the same reasoning can be applied with thread pools of size > 1 or dynamic size. 在我看来,选项2优于选项1 ,并且相同的推理可以应用于大小> 1或动态大小的线程池。

All I want is keep the thread pool as free as possible to spawn the lowest number of threads in order to avoid wasting resources. 我想要的是保持线程池尽可能的空闲,以产生最少数量的线程,以避免浪费资源。

Hope that makes sense. 希望有道理。 If not, please could you explain where I'm wrong? 如果没有,请您能解释我错了吗?

Instead of using thenApplyAsync , use thenCompose or thenComposeAsync , which lets the function return a CompletableFuture<Foo> instead of a Foo . 代替使用thenApplyAsync ,可以使用thenComposethenComposeAsync ,它们使函数返回CompletableFuture<Foo>而不是Foo And instead of return true if some condition is satisfied, you'll need to return CompletableFuture.completedFuture(true) . 如果需要满足some condition 需要return CompletableFuture.completedFuture(true) ,而不是return true

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // ... Some processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenComposeAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return CompletableFuture.completedFuture(false);

        // ... Some processing here ...

        if (!some condition satisfied) {
            return runSomeOtherQuery()
        }

        return CompletableFuture.completedFuture(true); // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // ... Some processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}


public CompletableFuture<Boolean> runSomeOtherQuery() {
    ....
}

It seems you are thinking that splitting work between chained stages (with “async” involved) somehow magically adds a concurrency improvement to you program logic. 似乎您正在考虑在链式阶段之间进行工作拆分(涉及“异步”)以某种方式神奇地为您的程序逻辑增加了并发性。

When you chain stages, you are creating a direct, sequential dependency, even if you use one of the “async” methods, as the execution of the subsequent, dependent stage doesn't start before the previous has completed. 链接阶段时,即使使用“异步”方法之一,也将创建直接的顺序依赖关系,因为后续的依赖阶段的执行不会在前一个依赖阶段完成之前开始。 So this kind of chaining adds the chance of expensive thread hopping, ie that a different thread executes the next stage, but without raising concurrency, as there still will be at most one thread processing one of your stages. 因此,这种链接增加了昂贵的线程跳跃的机会,即,另一个线程执行下一阶段,但又不提高并发性,因为最多会有一个线程处理您的一个阶段。 Actually, the still possible scenario that the same thread happens to execute all stages, is likely to be the fastest execution. 实际上,同一线程恰好执行所有阶段的可能性仍然是最快的。

There is a much simpler, natural way of expressing dependencies. 有一种表达依赖关系的简单得多的自然方法。 Just write the actions one after another in one piece of code. 只需在一段代码中一个接一个地编写动作。 You can still may schedule that code block for asynchronous execution. 您仍然可以安排该代码块进行异步执行。 So if your starting point is 因此,如果您的出发点是

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // First stage processing here ...
        if (somecondition failed)
            return false; // Task failed!

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // Second stage processing here ...

        if (!some condition satisfied) {
            // This is where I want the injection to happen. 
            // This stage should be suspended and a new stage should be
            // injected between this point and the next stage.
        }

        return true; // OK
    }).thenApplyAsync((Boolean result) -> {
        if (!result) // check of previous stage fail
            return false;

        // Third stage processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

just change it to 只需将其更改为

public CompletableFuture<Boolean> getFutureOfMyLongRunningTask() {
    CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {
        // First stage processing here ...
        if (somecondition failed)
            return false; // Task failed!
        // Second stage processing here ...

        if (!some condition satisfied) {
            // alternative "injected" stage processing
            if(injected stage failed)
                return false;
        }
        // Third stage processing here ...

        return true; // OK
    });

    // This is the result we have to wait for.
    return future;
}

which is shorter and clearer. 更短更清晰。 You don't have to repeatedly check for a previous stage's success. 您不必反复检查上一个阶段的成功。 You still have the same concurrency, but a potentially more efficient execution. 您仍然具有相同的并发性,但是执行效率可能更高。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM