异步子进程流式输出在CMD中顺序错乱问题求助
异步调用子进程时CMD中stdout输出顺序错乱(PyCharm中正常)
我编写了test.py和test1.py两个文件,test.py通过异步方式调用test1.py并在控制台展示其输出。在PyCharm中运行test.py时输出顺序正常,但在Command Prompt(CMD)中运行时,stdout内容未按预期顺序输出,出现了顺序错乱的情况。
test1.py代码
import logging import time logging.error(1) time.sleep(1) print(2) time.sleep(1) print(3) time.sleep(1) logging.error(4) time.sleep(1) logging.error(5) time.sleep(1) print(6)
test.py代码
import asyncio async def _read_stream(stream, cb): while True: line = await stream.readline() if line: cb(line) else: break async def _stream_subprocess(cmd, stdout_cb, stderr_cb): process = await asyncio.create_subprocess_exec(*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) stdout_task = asyncio.create_task(_read_stream(process.stdout, stdout_cb)) stderr_task = asyncio.create_task(_read_stream(process.stderr, stderr_cb)) await asyncio.wait([stdout_task, stderr_task]) return await process.wait() def execute(cmd, stdout_cb, stderr_cb): asyncio.run(_stream_subprocess(cmd, stdout_cb, stderr_cb)) if __name__ == '__main__': print(execute(["pipenv", "run", "python", 'test1.py'], lambda x: print("STDOUT: %s" % x), lambda x: print("STDERR: %s" % x),))
PyCharm中正常输出
STDERR: b'ERROR:root:1\r\n' STDOUT: b'2\r\n' STDOUT: b'3\r\n' STDERR: b'ERROR:root:4\r\n' STDERR: b'ERROR:root:5\r\n' STDOUT: b'6\r\n'
CMD中错乱输出
STDERR: b'ERROR:root:1\r\n' STDERR: b'ERROR:root:4\r\n' STDERR: b'ERROR:root:5\r\n' STDOUT: b'2\r\n' STDOUT: b'3\r\n' STDOUT: b'6\r\n'
问题原因与解决方法
原因
Python在终端环境下,stdout默认是行缓冲(仅在遇到换行符或缓冲区满时输出),而stderr是无缓冲(实时输出)。在CMD中,子进程的stdout输出会被缓冲,直到进程结束才一次性输出;而异步读取stderr的任务会实时处理输出,最终导致顺序错乱。PyCharm的终端环境默认禁用了stdout缓冲,因此输出顺序正常。
解决方法
有三种可行的方案:
方案1:运行Python时添加-u参数禁用缓冲
修改test.py中调用子进程的命令,添加-u参数强制Python禁用stdout和stderr的缓冲:
if __name__ == '__main__': print(execute(["pipenv", "run", "python", '-u', 'test1.py'], lambda x: print("STDOUT: %s" % x), lambda x: print("STDERR: %s" % x),))
方案2:在test1.py中手动刷新stdout
每次调用print时添加flush=True参数,强制刷新输出:
import logging import time logging.error(1) time.sleep(1) print(2, flush=True) time.sleep(1) print(3, flush=True) time.sleep(1) logging.error(4) time.sleep(1) logging.error(5) time.sleep(1) print(6, flush=True)
方案3:在test1.py开头全局禁用stdout缓冲
通过修改stdout的文件对象,设置无缓冲模式:
import logging import time import sys import os # 禁用stdout缓冲 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', buffering=0) logging.error(1) time.sleep(1) print(2) time.sleep(1) print(3) time.sleep(1) logging.error(4) time.sleep(1) logging.error(5) time.sleep(1) print(6)
内容的提问来源于stack exchange,提问作者j.p
相关产品推荐
相关产品推荐

