Python 队列和线程:子进程丢失

Python 队列和线程:子进程丢失,python,multithreading,video,subprocess,video-processing,Python,Multithreading,Video,Subprocess,Video Processing,早上好 我正在编写一个python守护进程,它需要将视频转换为.mp4格式。 为此,我计划通过子流程使用手刹,但结果喜忧参半:有时有效,有时流程甚至没有显示在顶部 我不知道发生了什么事。我尝试了一些变体,比如使用Shell=True,但问题仍然存在 谢谢 #! /usr/bin/python # -*- coding: utf-8 -*- import os, time, threading, psutil, resource, logging, subprocess as sp, sys f

早上好

我正在编写一个python守护进程,它需要将视频转换为.mp4格式。 为此,我计划通过子流程使用手刹,但结果喜忧参半:有时有效,有时流程甚至没有显示在顶部

我不知道发生了什么事。我尝试了一些变体,比如使用Shell=True,但问题仍然存在

谢谢

#! /usr/bin/python
# -*- coding: utf-8 -*- 
import os, time, threading, psutil, resource, logging, subprocess as sp, sys
from Queue import Queue
from threading import Thread

SERVER_LOG=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'convertCentral.log')
logging.basicConfig(format='[%(asctime)s.%(msecs).03d] %(message)s', datefmt='%Y-%m-%d %H:%M:%S', filename=SERVER_LOG, level=logging.INFO)

class QueueServer(object):

    current_video_queue = Queue(maxsize=0)
    N_WORKER_THREADS = 1
    counter = 0


    def __init__(self):
        print("[QueueServer] Initializing the video conversion queue")
        t = threading.Thread(target=self.monitor)
        t.start()


    ''' Alters the process' niceness in order to throtle the CPU usage '''
    def preexec_fn(self):
        pid = os.getpid()
        ps = psutil.Process(pid)
        ps.set_nice(10)
        resource.setrlimit(resource.RLIMIT_CPU, (1, 1))


    ''' Converts the video using Handbrake via subprocess'''
    def convertVideo(self, video):
        print("Now converting %s" % video)
        fileName, fileExtension = os.path.splitext(video)
        payload = "ulimit -t 360; nice -n 15 HandBrakeCLI -i %s -e x264 -q 15 -o %s.mp4" % (video, fileName)
        payload = payload.split(" ")

        # Fire in the hole
        pr = sp.Popen(payload, stdout=open('/dev/null', 'w'), stderr=sp.STDOUT)
        print("Fired.")
        pr.wait()

        self.counter = self.counter + 1
        print("Conversion's done. %d" % self.counter)


    ''' A worker thread '''
    def worker(self):
        while True:
            print("Getting one")
            item = self.current_video_queue.get()
            print("Firing conversion: %s" % item)
            self.convertVideo(item)
            self.current_video_queue.task_done()
            print("All done")


    def monitor(self):
        for i in range(self.N_WORKER_THREADS):
            print("Firing thread")
            t = Thread(target=self.worker)
            t.daemon = True
            t.start()


    ''' Adds a video to the video conversion queue '''
    def add(self, video):
        print("* Adding %s  to the queue" % video)
        self.current_video_queue.put(video)
        print("* Added %s  to the queue" % video)


q = QueueServer()
q.add('UNKNOWN_PARAMETER_VALUE.WMV')
#time.sleep(500)
以下是我从日志中得到的信息:

Hal@ubuntu:~/Desktop/$ python threadedqueue.py
[QueueServer] Initializing the video conversion queue
Firing thread
* Adding UNKNOWN_PARAMETER_VALUE.WMV  to the queue
* Added UNKNOWN_PARAMETER_VALUE.WMV  to the queue
Getting one
Firing conversion: UNKNOWN_PARAMETER_VALUE.WMV
Now converting UNKNOWN_PARAMETER_VALUE.WMV
所以,我们可以看出子流程实际上在运行,但它只是神秘地消失在那里。。。
你知道这是什么原因吗?

首先,你提供的有效载荷可能不是你想要的

在正常操作中,子流程不会向shell发出命令,而是启动名称为args[0]的进程,并将所有其他参数直接传递给它。您要做的是传递以下参数:

["-t", "360;", "nice", "-n", "15", "HandBrakeCLI", "-i", "someInput", "-e", "x264", "-q", "15", "-o", "someOutput.mp4"]
。。。一个非常混乱的ulimit过程。 相反,您需要使用shell=True,并将args作为字符串提供。这告诉Popen启动一个shell并将其作为一行程序转储,而不是实际启动您请求的进程

payload = "ulimit -t 360; nice -n 15 HandBrakeCLI -i %s -e x264 -q 15 -o %s.mp4" % (video, fileName)
pr = sp.Popen(payload, shell=True, stdout=sp.DEVNULL, stderr=sp.STDOUT)
print('Started handbrake')
pr.wait()
如果你没有打印“启动手刹”的话,那就要料到在波本发生了可怕的错误。调试它的一个好方法是获取有效负载,导入子流程,并尝试在交互式控制台上使用Popen打开它。如果您被锁定在Popen呼叫中,ctrl-c应该会给您一个跟踪,跟踪您挂在其中的位置,这反过来可能会为您提供一些帮助。即使这对你来说毫无意义,它也可能会帮助别人知道发生了什么


我希望这是有帮助的

您可以在Python中模拟
ulimit-t
nice-n
,在不使用shell的情况下运行
HandBrakeCLI

#!/usr/bin/env python
import os
import resource
import shlex
from subprocess import call, STDOUT

DEVNULL = open(os.devnull, 'wb', 0)

# quote filenames to allows names with spaces
path, output_path = map(shlex.quote, [video, filename + '.mp4'])
cmd = "HandBrakeCLI -i {path} -e x264 -q 15 -o {output_path}".format(**vars())

def limit_resources():
    resource.setrlimit(resource.RLIMIT_CPU, (360, 360)) # emulate ulimit -t
    os.nice(15) # emulate nice -n

rc = call(shlex.split(cmd), stdout=DEVNULL, stderr=STDOUT,
          preexec_fn=limit_resources)

嗨,谢谢你的帮助!我设置了shell=True参数并对其进行了测试,结果也是喜忧参半。。。但是,如果我在交互控制台中运行子流程片段,它将完美地工作!似乎它与队列有关。好吧,队列会使您无法获取作业-您的输出使其看起来好像获取了作业,但在Popen上“暂停”。它是否在新的Popen通话中打印过“启动手刹”/“点火”?如果没有,您可以执行printf风格的调试(到处打印输出),或者使用
import pdb启动Python调试器;pdb.set_trace()
在最后一行“good”之后,这将允许您单步执行代码(下一行到下一行,步骤应进入函数…我认为它有一个帮助页面)。这将允许您查看暂停/中断的位置。添加时间。睡眠(5)在q.add行解决了这个问题之后。虽然这部分已经解决了,但我仍然在与守护进程的其余部分进行斗争。所有消息都会显示,但pr.wait()是即时的,并且会失败。我想我需要为这个想法打开另一个问题注:
ulimit
是一个内置的shell命令,也就是说,如果没有
shell=True
,即使没有额外的未知参数,它也会失败。我将在周一试一试,因为我现在不在家,但我认为ulimit和nice不是问题所在。无论如何,我以后会给你一些反馈。谢谢@哈尔:我还认为,
ulimit,nice
不是你代码中唯一的问题。我应该明确地提到它。1.确保代码在单个线程中工作,例如,在引入多线程2之前,检查
RLIMIT\u CPU
是否在您的系统上工作。始终选中
rc!=0
(如果可以接受在HandBrakeCLI失败时引发异常,则可以使用
检查调用()
)3。您可以在一个线程中运行所有代码,因为
Popen()
不会阻塞主线程(尽管为了方便起见,您可以使用多个线程)