函数中的Popen阻止该函数在单独的线程中执行

2024-10-16 20:43:56 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试从一个python程序(在Linux下)执行一个二进制文件,并希望将其专用于一个单独的线程。编辑:二进制文件将运行一段时间并将数据写入磁盘。如果我能用python检索stdout和stderr,这样我就可以把它写进日志了。如果我能计算二进制文件的返回值以确保它成功,那也太好了

然而,当我用线程启动的函数包含Popen语句时,线程似乎根本就没有启动:-(这是我不理解的关于线程安全的奇怪事情吗?一旦我注释掉调用外部命令的部分,函数就被称为fine

下面是我的代码的最低版本,我使用的是python 2.7.9:

from subprocess import Popen, PIPE
import subprocess
import sys
from threading import Thread


def record ( _command):
    print "Starting function"
    print "Now executing the following command: " + _command
    sys.stdout.flush()

#Here starts the interesting part. As long as this parts is part of the function, the function is not started at all.
#Not even the two lines before are executed :-(
#If I comment out the following lines, at least the print statements work
    process = Popen([_command], stdout=PIPE, stderr=PIPE, shell=True)
    stdout_lines = iter(process.stdout.readline, "")
    for stdout_line in stdout_lines:
        yield stdout_line

    process.stdout.close()
    return_code = process.wait()
    if return_code != 0:
        raise subprocess.CalledProcessError(return_code, _command)


###########################
#Main function starts here#
###########################
threads=[]
for i in range (0,5):
    t=Thread(target=record,args=('ls',))
    threads.append(t)
    t.start()

# wait for all threads to finish
for t in threads:
    t.join()
print "Exiting skript"

这个代码只是打印出来的:

Exiting skript

我一评论完波本部分,就得到:

Starting function
Now executing the following command: ls
Starting function
Now executing the following command: ls
Starting function
Now executing the following command: ls
Starting function
Now executing the following command: ls

启动功能 现在执行以下命令:ls

Exiting skript

Tags: theimportforstdoutfunction线程processls
2条回答

您的record函数不能按预期工作不是因为Popen调用,而是因为它是一个生成器函数(因为它包含一个yield语句)。生成器函数在调用它们时实际上并不运行它们的代码。相反,它们会立即返回一个生成器对象,并且函数中的代码只有在对生成器对象进行迭代时才会运行

我认为没有任何有用的方法可以直接调用生成器函数作为线程的目标。相反,您可能希望线程以使用生成器对象的其他函数为目标。或者可以将当前函数重新构造为不是生成器(不清楚yield语句的目的是什么)

通过一些小的修复,您的代码可以运行:

  • 去掉了生成器函数,对线程没有意义
  • 简化了从进程中获取输出的方法,但仍然是逐行读取(使用communicate读取最终输出可能会起作用,但您似乎需要立即打印行以了解当前的进度)
  • 使用Lock保护输出,否则会在线程之间混淆
  • 在管道中合并stdout和stderr以避免死锁

固定代码:

from subprocess import Popen, PIPE, STDOUT
import subprocess
import sys
from threading import Thread,Lock

lck = Lock()

def record ( _command):
    lck.acquire()
    print("Starting function")
    print("Now executing the following command: " + _command)
    lck.release()

    process = Popen([_command], stdout=PIPE, stderr=STDOUT, shell=True)
    while True:
        line = process.stdout.readline()
        if len(line)==0:
            break
        lck.acquire()
        sys.stdout.write(line)
        lck.release()

    process.wait()
    return_code = process.wait()
    if return_code != 0:
        raise subprocess.CalledProcessError(return_code, _command)

# main program

threads=[]
for i in range (0,5):
      t=Thread(target=record,args=('ls',))
      threads.append(t)
      t.start()

# wait for all threads to finish
for t in threads:
      t.join()
print("Exiting skript")

相关问题 更多 >