我编写了一个脚本来操作国际象棋引擎的标准输入和标准输出。基本上,我使用 subprocess.popen 打开程序,并使用 threading.Thread 操作每个输入和输出。 但是,如果生成了我的脚本的多个实例,程序将在大约 15 分钟后停止工作。我认为这是由于文档描述的缓冲区已满而导致的死锁。不幸的是,我无法使用推荐的“通信”功能,因为我需要不断修改输入和输出。有什么办法可以处理吗?这是我所做的最小版本:
import sys
import time
import subprocess
import threading
class ChessEngine:
def __init__(self):
self.lock = threading.Lock()
def write(self, command):
with self.lock:
self.engine.stdin.write(command + "\n")
self.engine.stdin.flush()
def read_stdout(self):
while True:
text = self.engine.stdout.readline()
text = text.replace("foo", "bar")
print(text, end="", flush=True)
def read_stdin(self):
while True:
command = input()
command = command.replace("foo", "bar")
self.write(command)
def run(self):
self.engine = subprocess.Popen(
"/usr/games/stockfish",
shell=True,
universal_newlines=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1,
cwd=self.folder,
)
threading.Thread(target=self.read_stdout, daemon=True).start()
threading.Thread(target=self.read_stdin, daemon=True).start()
while True:
time.sleep(1)
if not self.engine.poll() is None:
sys.exit(0)
if __name__ == "__main__":
chess_engine = ChessEngine()
chess_engine.run()
我寻找了几种方法来确保脚本不会阻塞 - 但没有成功。
似乎真正的问题是 stderr 缓冲区已满,因为您没有读取它。 所以,如果你不读stderr,你可以丢弃它:
self.engine = subprocess.Popen(
"/usr/games/stockfish",
shell=True,
universal_newlines=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, # DISCARD IT!
bufsize=1,
cwd=self.folder,
)
在这种情况下,缓冲区不会填满。
您还可以将 stderr 重定向到 stdout,就像您在
read_stdin
中阅读的那样:
self.engine = subprocess.Popen(
"/usr/games/stockfish",
shell=True,
universal_newlines=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # REDIRECT IT
bufsize=1,
cwd=self.folder,
)
这个解决方案也可以工作。
最后你可以开始在另一个线程中阅读你的stderr:
def read_stderr(self):
while True:
text = self.engine.stderr.readline()
...
pass
def run(self):
self.engine = subprocess.Popen(
"/usr/games/stockfish",
shell=True,
universal_newlines=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1,
cwd=self.folder,
)
threading.Thread(target=self.read_stdout, daemon=True).start()
threading.Thread(target=self.read_stdin, daemon=True).start()
threading.Thread(target=self.read_stderr, daemon=True).start() # START REEAD THIS
...