Python:Java子进程和Python监听器之间的多线程?

时间:2014-11-21 00:13:01

标签: java python multithreading

我正在监视和Minecraft服务器,我正在使用Python创建一个安装文件。我需要能够运行两个线程,一个在控制台窗口中运行minecraft_server.jar,而另一个线程不断检查minecraft_server的输出。另外,在启动Java进程后,如何从Python输入控制台?

示例:

thread1 = threading.Thread(target=listener)
thread2 = minecraft_server.jar

def listener():
    if minecraft_server.jarOutput == "Server can't keep up!":
        sendToTheJavaProccessAsUserInputSomeCommandsToRestartTheServer

1 个答案:

答案 0 :(得分:2)

这里很难说,但我你问的是如何:

  • 在后台启动程序。
  • 发送输入,就像它来自控制台上的用户一样。
  • 读取它尝试在控制台上向用户显示的输出。
  • 同时,运行另一个执行其他操作的线程。

最后一个很容易;事实上,你大部分时间都在编写它,你只需要在某处添加thread1.start()

subprocess模块允许您启动程序并控制其输入和输出。如果您想立即输入所有输入,等待它完成,然后处理所有输出,这是最简单的,但显然这不是你的情况,所以它涉及更多:

minecraft = subprocess.Popen(['java', 'path/to/minecraft_server.jar', '-other', 'args],
                             stdin=subprocess.PIPE, 
                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

我将stdout和stderr合并成一个管道;如果你想单独阅读它们,或者将stderr发送到/ dev / null,或者其他什么,请参阅文档;这一切都非常简单。虽然我们在这里做出假设,但我将假设minecraft_server使用简单的基于行的协议,其中每个命令,每个响应和每个信息消息恰好是一行(即,低于1K)以\n开头的文字。

现在,要发送输入,您只需执行此操作:

minecraft.stdin.write('Make me a sandwich\n')

或者,在Python 3.x中:

minecraft.stdin.write(b'Make me a sandwich\n')

要阅读其输出,请执行以下操作:

response = minecraft.stdout.readline()

这就像普通文件一样。但请注意,它的工作方式类似于二进制文件。在Python 2.x中,唯一的区别是换行不会自动转换,但在Python 3.x中,这意味着您只能编写bytes(和兼容的对象),而不是str s ,您将收到bytes返回。这有很好的理由,但是如果您希望获得与文本文件类似的管道,请参阅Frequently Used ArgumentsPopen Constructor下的universal_newlines(以及可能的bufsize)参数


此外,它的工作方式类似于阻止文件。对于常规文件,这很少重要,但是使用管道,很可能以后会有数据,但还没有数据(因为服务器还没有编写它)。所以,如果还没有输出(或者不是完整的行,因为我使用了readline()),你的线程就会阻塞,等到有。

如果您不想这样,您可能想要创建另一个线程来为stdout提供服务。它的功能实际上看起来与你所拥有的相似:

def listener():
    for line in minecraft.stdout:
        if line.strip() == "Server can't keep up!":
            minecraft.stdin.write("Restart Universe\n")

现在该线程可以整天阻塞并且没有问题,因为你的其他线程仍在继续。


好吧,不是没有问题。

首先,很难干净地关闭你的程序。

更严重的是,流程之间的管道具有固定的大小;如果你没有足够快地为stdout提供服务,或者孩子没有足够快地服务stdin,那么管道就会阻塞。而且,我写的东西的方式,如果stdin管道阻塞,我们将永远被stdin.write阻止,并且不会进入下一次读取stdout,所以这也可以阻止,突然间我们都在一起等待。

您可以通过另一个线程来为stdout提供服务来解决这个问题。 subprocess模块本身包含所有高级函数使用的Popen._communicate函数中的示例。 (确保查看Python 3.3或更高版本,因为早期版本存在错误。)

如果您使用的是Python 3.4+(或3.3,带有PyPI的backport),您可以改为使用asyncio围绕事件循环重写程序,并按照您的方式处理输入和输出写一个基于反应堆的网络服务器。这就是所有酷孩子在2017年所做的事情,但是在2014年底,许多人仍然认为它看起来很新而且很可怕。


如果所有这些听起来比你签署的工作要多得多,你可能要考虑使用pexpect,它包含了很多繁琐的细节,并做了一些可能的简化假设在你的情况下是真的。