简述
串行执行异步任务
使用vertx框架编程的过程中,经常会遇到这种情况:需要处理一组异步任务,而且我们希望它们可以按照顺序执行下去,并且当它们全部执行完了得到通知,而且有时候也需要控制,当一个执行失败,后面的任务是否需要继续执行。这个就是按照顺序去执行异步任务,解决方案当然就是递归了,这个在我很早的博客当中已经写过了。多个异步请求保证执行顺序:用递归。但是呢,随着功能的一步步增多,这种需求是特别多的,你需要写大量的递归,我写着都烦了,那能不能封装一个执行器,替我们来递归执行任务呢,我们只需要按顺序提交并且提供一个回调处理器呢,答案是有的。
并行执行异步任务
另外一种场景:也是需要处理一组异步任务,但是它们是无序的,无序执行异步任务本身是很简单的,用普通的for循环就行了,但是最难的地方在于,它没有办法回调,就是不知道什么时候全部执行了。我们的需求就是需要执行一个回调器,全部执行完了以后通知我,并且告诉我,有哪些失败了,我再进一步处理。我把这个实现了以后,又去思考了上面的场景,也顺便实现了一下。
并行执行任务设计和实现
并行设计
想要实现这个,就是从下面几个方面去入手就行了:
- vertx提交的异步任务,真正的执行都会在另外一个线程里面(包括本身线程),至于说最终线程怎么执行的,我们其实不关心,关心的就是把这些任务一次性全部提交了,这个容易,for循环遍历一下就行了。
- 我们重点想要的是一个通知,全部执行完成的通知,那也就是应该当每一个任务执行完成以后,都应该告诉执行器,我执行完了,不管失败还是成功,然后执行器再去校验是否全部都通知过了,以此来判断是否发出最后的通知。那也就是说,对于提交的每一个任务,都有一个Future(在vertx里面代表一个异步操作的结果),用来表示它的状态,并且当完成以后,它的回调处理器就应该去校验所有的Future。
- 当所有都执行完毕了,我们就要去回调,回调什么呢?一个处理器,由用户提供,并且也是一个异步结果。
- 还有就是执行结果了,要么成功要么失败。暂时就定义成:全部成功算成功,一个失败就算失败。如果有多个失败的,但是我们只能抛一个异常,那就在一个异常里面把所有的错误情况都添加进去。
并行实现
根据上面的思考,就有了下面的实现,直接贴代码了。
/**
* @Description 并行异步任务执行器
* 用来解决场景:处理一组异步任务,任务之间没有顺序,但是希望在执行全部结束以后,进行回调。
* 重点是提供回调的功能。因为普通的for循环就能够不按顺序执行它们,但是没有回调。
*
**/
public class ParallelAsyncTaskExecutor implements AsyncResult<Void> {
/**
* 存储所有任务
* key:任务。
* value:是任务对应的异步结果。
*/
private final Map<Handler<Future<Void>>, Future<Void>> tasks;
private Handler<AsyncResult<Void>> handler;
public ParallelAsyncTaskExecutor() {
this.tasks = new HashMap<>();
}
/**
* 添加一个异步任务,给这个任务挂上一个处理器,当任务执行以后就去校验是否所有的都执行完了。
* @param handler
*/
public void addAsyncTask(Handler<Future<Void>> handler){
Future<Void> future = Future.future();
future.setHandler(futureHandler -> {
checkCallHandler();
});
this.tasks.put(handler, future);
}
/**
* 检查并调用处理器
*/
private void checkCallHandler() {
if (isComplete()) {
callHandler();
}
}
private void callHandler(){
if (handler != null) {
handler.handle(this);
handler = null;
}
}
/**
* 是否所有任务完成了
* 挨个判断每个任务的结果。
* @return
*/
public boolean isComplete() {
if (tasks.isEmpty()) {
return false;
}
return tasks.values().stream().allMatch(future -> {
return future.isComplete();
});
}
/**
* 执行启动。
* for循环执行
*/
public void start() {
if (this.tasks.isEmpty()) {
callHandler();
return;
}
this.tasks.forEach((handler, future) -> {
try {
handler.handle(future)