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

Renderfarm开发适配:subprocess执行逻辑与多命令顺序需求

渲染农场Server端问题及解决方案

当前存在的问题

  • 使用subprocess.Popen(self.data)可执行多条命令,但无执行顺序,测试函数Le_Printes()会立即触发,无法完成后续渲染文件迁移操作;
  • 使用subprocess.run/call/check_call可顺序执行命令,但服务器仅支持单进程处理,一次只能处理一个任务,必须重启才能接收新命令。

需求

需要实现类似subprocess.Popen(self.data, afterFunction= MyTransferFunction())的回调功能,或者让subprocess.call支持多线程,同时接收并处理多条命令。


客户端代码

ShotName = MayaRenderPath.split("/")[-3]
DesktopMayaRenderPath = HostName + "/MayaRenders/" + ShotName
print("HostDesktopMayaRenderPathName = {}".format(DesktopMayaRenderPath))

RoyalCommand = Constant + Constant2 + DesktopMayaRenderPath + " " + FullMayaPath
print("RoyalCommand = {}".format(RoyalCommand))

##########################################
#os.system(RoyalCommand)
HOST, PORT = "10.100.20.139", 9999
#data = " ".join(sys.argv[1:])
data = RoyalCommand
dmrp = DesktopMayaRenderPath
MayaPateshestvie = r"{}".format(MayaRenderPath)

# 创建TCP Socket连接
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # 连接服务器并发送数据
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data, "utf-8"))
    sock.sendall(bytes("\n".join([dmrp, MayaPateshestvie]), "utf-8"))

    # 接收服务器响应
    received = str(sock.recv(2048), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))

##########################################
#def MyTransferFunction():
#for folder in os.listdir(DesktopMayaRenderPath):
#    shutil.move(DesktopMayaRenderPath + "/" + folder,MayaRenderPath)
#    shutil.move(os.path.join(DesktopMayaRenderPath,folder),MayaRenderPath)
#    try:
#        shutil.rmtree(os.path.join(MayaRenderPath,folder),ignore_errors=True)
#        shutil.move(os.path.join(DesktopMayaRenderPath,folder),MayaRenderPath)
#    except: 
#        pass

服务端代码

from asyncio import constants
from asyncio.subprocess import STDOUT
from operator import sub
import socketserver
import os,subprocess
class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    服务器请求处理类,每个客户端连接实例化一次,
    必须重写handle()方法实现与客户端的通信逻辑。
    """
    def Le_Printes(self):
        print("Le Finished")

    def handle(self):
        # self.request是与客户端连接的TCP Socket
        self.data = self.request.recv(2048).strip().decode('utf-8')
        self.dmrp, self.mrp = [i for i in self.request.recv(2048).decode('utf-8').split('\n')]
        print("{} wrote:".format(self.client_address[0]))
        print("RoyalCommand = {}".format(self.data))
        print("DesktopMayaRenderPath = {}".format(self.dmrp))
        print("MayaRenderPath = {}".format(self.mrp))

        self.HostName = os.path.expanduser("~")
        self.HostName = self.HostName.replace("\\","/") + "/Desktop"
        print("HostName = {}".format(self.HostName))

        subprocess.Popen(self.data)
        self.Le_Printes()
        

if __name__ == "__main__":
    HOST, PORT = "10.100.20.139", 9999

    # 创建服务器,绑定到指定IP和端口
    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        # 启动服务器,持续运行直到Ctrl-C中断
        server.serve_forever()

解决方案

方案1:为Popen添加回调逻辑

通过线程等待Popen进程执行完毕,再触发文件迁移函数,既保留多进程处理能力,又保证操作顺序。修改服务端代码如下:

首先添加依赖和迁移函数:

import threading
import shutil

def MyTransferFunction(dmrp, mrp):
    """渲染完成后执行文件迁移"""
    for folder in os.listdir(dmrp):
        src_folder = os.path.join(dmrp, folder)
        dst_folder = os.path.join(mrp, folder)
        try:
            shutil.rmtree(dst_folder, ignore_errors=True)
            shutil.move(src_folder, mrp)
            print(f"已迁移文件夹: {src_folder} -> {mrp}")
        except Exception as e:
            print(f"迁移文件出错: {str(e)}")

def run_with_callback(cmd, callback, *args):
    """执行命令后调用回调函数"""
    proc = subprocess.Popen(cmd, shell=True)
    proc.wait()  # 等待命令执行完成
    callback(*args)

然后修改handle方法:

def handle(self):
    # 原有接收数据的代码保持不变

    # 启动独立线程执行命令和回调,不阻塞服务器接收新连接
    threading.Thread(
        target=run_with_callback,
        args=(self.data, MyTransferFunction, self.dmrp, self.mrp)
    ).start()
    self.Le_Printes()
    # 可选:向客户端发送任务已启动的响应
    self.request.sendall(b"任务已启动,将在完成后自动迁移文件")

方案2:使用多线程版TCPServer

默认TCPServer是单线程的,改用ThreadingTCPServer即可同时处理多个客户端连接,配合subprocess.run实现单任务内的顺序执行:

修改服务端主函数:

if __name__ == "__main__":
    HOST, PORT = "10.100.20.139", 9999

    # 使用ThreadingTCPServer替代单线程TCPServer
    with socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()

修改handle方法,使用subprocess.run顺序执行:

def handle(self):
    # 原有接收数据的代码保持不变

    try:
        # 顺序执行渲染命令,完成后执行迁移
        subprocess.run(self.data, shell=True, check=True)
        MyTransferFunction(self.dmrp, self.mrp)
        self.request.sendall(b"渲染及文件迁移完成")
    except subprocess.CalledProcessError as e:
        self.request.sendall(f"渲染命令执行失败: {str(e)}".encode('utf-8'))
    except Exception as e:
        self.request.sendall(f"任务执行出错: {str(e)}".encode('utf-8'))
    self.Le_Printes()

注意事项

  • 使用shell=True时需确保命令来源安全,避免命令注入风险;如果命令由多个参数组成,建议拆分列表形式传入subprocess(如subprocess.Popen(["maya", "-render", ...]))。
  • 方案1适合高并发场景,每个任务在独立线程中执行;方案2实现更简单,适合对任务执行顺序要求严格的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 10:54:20