You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows下Rust启动Python子进程时stdin读取阻塞问题求助

Windows下Rust启动Python时子进程阻塞问题

最小可复现示例

我准备了可克隆运行的最小复现示例用于测试,完整代码将在本文后续部分提供。

问题简述

在Windows系统中通过Rust启动Python进程时,读取stdin会导致Python子进程无法正常启动更多的Python子进程,Linux下无此问题。添加sleep(1)能临时让子进程正常启动,但如果子进程启动耗时超过1秒则失效,耗时不足1秒又会额外等待,因此sleep只是临时补丁,需要寻求可靠的解决方案。

问题详述

触发场景及背景

该问题仅在Windows环境下由Rust启动Python时出现,直接运行Python程序一切正常。我的应用是基于Tauri的Rust GUI程序,由于存在一个无法移植到Rust(且大概率永远不会移植)的大型Python专属库,因此引入了Python子组件。

这意味着在生成子进程期间,主Rust进程无法处理其他无关命令,因为此时它并未读取stdin。实际程序中我通过多线程启动新线程,理论上生成子进程的同时可以读取stdin,但现在必须避免在子进程生成期间读取stdin,增加了不必要的复杂度。

预期与实际行为差异

预期中say-hi.py应持续输出"Hello World",但实际子进程虽已生成却未开始执行。我确定该问题与stdin相关——程序通过stdin和stdout实现主Rust进程与Python进程间的通信。

主线程读取stdin会以某种方式阻止子进程完全启动。若生成子进程时设置shell=True,则在主循环结束后"Hello World"才会持续输出,且无法停止。

另外,在生成子进程后立即添加sleep(1)可暂时解决问题,但这绝非理想方案。我已在程序中临时使用sleep(3),但如果需要生成数十个子进程,总耗时会过长,且仍存在子进程启动耗时超过3秒导致失效的可能。

问题代码(仅Windows下出现!)

以下代码移除了import语句等无关内容,完整逻辑可参考完整示例。

main.rs

// Setup command
let mut command = Command::new("python");
command.args(["-u", "-c", "from main import start; start()"]);
command.current_dir(std::env::current_dir()?.join("python"));

// Pipe stdin so we can issue commands to tell Python what we want it to do
command.stdin(Stdio::piped());

// Spawn the process & take ownership of stdin
let mut child = command.spawn()?;
let mut stdin = child.stdin.take().unwrap();

// Give a second to make 100% sure Python has started
sleep(Duration::from_secs(1));

// Issue start command to Python, wait 5 seconds then tell it to stop

println!("Writing start");
stdin.write_all(b"start | 
")?;

sleep(Duration::from_secs(5));

println!("Writing stop");
stdin.write_all(b"stop | 
")?;

// Make sure the process has exited before we exit
child.wait()?;

main.py

def start():
    procs = []
    while True:
        print("Listening for new command...")
        command = sys.stdin.readline()
        params = command.split(" | ")
        print(f"Got command: {command}")

        if params[0] == "stop":
            print("Stopping...")
            # Break from loop
            break
        elif params[0] == "start":
            print("Starting...")
            # Spawn the subprocess
            proc = subprocess.Popen([sys.executable, "say-hi.py"])

            # Add the subprocess to the list so we can kill it later
            procs.append(proc)

    print("Shutting down")
    # Terminate processes
    for proc in procs:
        proc.terminate()

say-hi.py

while True:
    print(f"Hello world!", flush=True)
    sleep(1)

运行步骤

以下步骤与示例仓库中的说明一致:

根目录下

  • 运行cargo r,无任何输出
  • 修改python/main.py中的Popen([sys.executable, "say-hi.py"])为Popen([sys.executable, "say-hi.py"], shell=True)
  • 运行cargo r,发出stop命令后"Hello world"会持续输出且无法停止

python目录下

  • shell设为True或False均不影响
  • 运行python -c "from main import start; start()"启动进程
  • 输入start | ,"Hello world"会持续输出
  • 输入stop | ,"Hello world"停止输出,所有进程正常退出

内容的提问来源于stack exchange,提问作者VirxEC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 21:45:38