作者:宇剑小窝_911 | 来源:互联网 | 2023-10-11 12:15
上周的工作中遇到了一个停止运行任务时的问题,就是在一个任务运行到一半时需要停止的问题。正常的需求是一按停止按钮,就要立即停止,但现在都要等程序运行完才能停止,现在是一个bug。由于之前写任务运行逻辑的不是我,因此我先花一个小时苦读大神们的代码,了解了他们对于任务运行那块的逻辑,发现是首先前端发消息给一个Actor,然后这个Actor用actorOf方法创建许多子Actor,然后再对这些Actor调用ActorSelection创建运行这个任务的Actor。
之前以为通过获取Actor的地址,终止这个Actor,然后自动终止其所有子Actor应该就能停止任务了,后来发现根本停不下来。
第一次尝试是在状态机FSM中不仅在whenUnhandled方式下停止Actor,而且在其他每一种状态下都调用停止Actor操作,直接终止运行该线程的Actor。
这里顺便普及一下停止Actor的时候,是给该Actor发送两种消息,Kill或者PoisonPill。使用Kill消息可以在不损失缓冲区中消息的情况下重启Actor对象,但是不会停止Actor对象,仅仅是重启,而且会抛出异常,使用PoisonPill消息可以停止Actor对象,但允许Actor对象处理完收到PoisonPill消息之前存储在缓冲区中的消息。然后我又尝试了context.stop(actorRef)去直接停止某Actor,同样无济于事,线程依旧潇洒地运行结束。
这个时候我发现问题应该转移到线程级别,既然终止Actor是不起作用的,我就干脆把这个线程终止掉。一开始我们采用给运行线程的Actor发送消息的方式终止该线程,发现能停止,但还是在任务运行完之后停止。后来我意识到,Actor处理消息应该是按照发送顺序来的,首先发送的消息是处理这个任务,所以只有处理完任务才会处理停止线程这个消息,此时线程已经不是运行状态了。因此我干脆定义了一个线程安全的HashMap,保存了id和Process对象的映射关系,可以通过这个数据结构搜索到线程对象,当父Actor收到终止任务命令时,不再向运行该任务的子Actor发送消息,而是在父Actor中直接调用运行任务的线程的Process对象里的destroy方法,直接终止这个线程,终于成功了。
得出结论就是,akka系统对于线程粒度的启停控制可能还是有一定缺陷,需要我们根据线程的Process对象自己来做停止操作。