java采用Process.destroy无法停止子进程
問題描述
當(dāng)前的應(yīng)用場景是,采用java執(zhí)行一個(gè)python腳本,該腳本是一個(gè)機(jī)器學(xué)習(xí)算法,在訓(xùn)練中會(huì)比較耗時(shí)。在某些場景下,需要采用java的進(jìn)程主動(dòng)停止該python進(jìn)程。采用如下思路進(jìn)行該邏輯實(shí)現(xiàn)。
一種錯(cuò)誤的實(shí)現(xiàn)
啟動(dòng)進(jìn)程
ProcessBuilder builder = new ProcessBuilder("/bin/sh", "-c", command);TaskExecutor executor = privpyClient.getTaskExecutor(requestId);builder.redirectErrorStream(true);log.info("{} enter running the code: {}", requestId, command);Process process = null;try {process = builder.start();if (Objects.nonNull(executor)) {executor.setPlainProcess(process);}log.info("{} check if the code is started {}!", requestId, process.isAlive());} catch (IOException e) {log.error("{} start the process {} failed!,error:{}", requestId, command, e);return false;}其中command就是“python3 test.py”的啟動(dòng)python進(jìn)程的語句。
停止進(jìn)程
Field f = process.getClass().getDeclaredField("pid"); f.setAccessible(true); long pid = f.getLong(process) + 1; log.info("{} kill process pid {} start", requestId, pid); process.destroy();這里不需看細(xì)節(jié),其實(shí)就是將對應(yīng)的process對象先進(jìn)行了存儲(chǔ),之后在主動(dòng)停止時(shí)進(jìn)行了一次調(diào)用。
其實(shí)其中的pid+1已經(jīng)一定程度暴露了信息,因?yàn)槲覀儼l(fā)現(xiàn)真正的python進(jìn)程號(hào)總是與process對象拿到的差1。
例如,我們從process拿到的pid為307,然python的進(jìn)程號(hào)是308
效果
執(zhí)行完process.destroy()后,對應(yīng)的307進(jìn)程退出,但是被拉起的308號(hào)python進(jìn)程仍舊在系統(tǒng)中好好的存在。
問題思考
1、process作為java的進(jìn)程抽象類,其真正作用是在java進(jìn)程中再啟動(dòng)一個(gè)子的java進(jìn)程;這個(gè)停止動(dòng)作也只能作用到這個(gè)主進(jìn)程;
2、其中的python進(jìn)程比啟動(dòng)的子進(jìn)程大1,因此可以從這個(gè)邏輯出發(fā),在java停止掉process時(shí),再發(fā)送一個(gè)系統(tǒng)調(diào)用的停止信號(hào)。
最后實(shí)現(xiàn)如下
public static void killPlainProcess(Process process, String requestId) throws Exception {Field f = process.getClass().getDeclaredField("pid");f.setAccessible(true);long pid = f.getLong(process) + 1;log.info("{} kill process pid {} start", requestId, pid);killChildProcess(pid);process.destroy();log.info("{} kill process pid {} finished ", requestId, pid); }/*** mind here, the process usd for call the kill should be clear too!!** @param pidNum*/ private static void killChildProcess(long pidNum) throws Exception {String cmd = "kill -15 " + pidNum;try {Process killProcess = null;killProcess = Runtime.getRuntime().exec(cmd);killProcess.waitFor();TimeUnit.MILLISECONDS.sleep(100);killProcess.destroy();} catch (IOException | InterruptedException e) {throw e;} }注意點(diǎn):在又啟動(dòng)了一個(gè)process來執(zhí)行kill -15信號(hào)后,需要注意將自己也執(zhí)行killProcess.destroy();做好資源清理。
但是發(fā)現(xiàn)多次執(zhí)行后,存在不穩(wěn)定情況,會(huì)導(dǎo)致主進(jìn)程掛掉。
正確拉起python進(jìn)程的實(shí)現(xiàn)
#### 啟動(dòng)進(jìn)程 ProcessBuilder builder = new ProcessBuilder("Python", command); TaskExecutor executor = privpyClient.getTaskExecutor(requestId); builder.redirectErrorStream(true); log.info("{} enter running the code: {}", requestId, command); Process process = null; try {process = builder.start();if (Objects.nonNull(executor)) {executor.setPlainProcess(process);}log.info("{} check if the code is started {}!", requestId, process.isAlive()); } catch (IOException e) {log.error("{} start the process {} failed!,error:{}", requestId, command, e);return false; }其中command就是“test.py”。
總結(jié)
以上是生活随笔為你收集整理的java采用Process.destroy无法停止子进程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: druid源码学习2-DruidData
- 下一篇: 「环卫吸粪车」天河区抽化粪池抽泥浆抽污水