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

时间:2016-10-08 16:39:32

标签: python multithreading

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

但是,一旦我从线程开始的函数包含Popen语句,该线程似乎根本没有开始:-(这是一个关于线程安全的奇怪的事情,我不明白?当我注释掉调用外部命令的部分时,该函数被称为正常....

这是我的代码的最低版本,我使用的是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

当我评论Popen部分时,我得到:

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

2 个答案:

答案 0 :(得分:1)

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

我认为没有任何有用的方法可以将生成器函数直接调用为线程的目标。相反,您可能希望该线程针对一些消耗生成器对象的其他函数。或者您可以将当前函数重构为不是生成器(yield语句的目的并不明显。)

答案 1 :(得分:1)

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

  • 摆脱了生成器功能,用线程没有意义
  • 简化了从流程中获取输出的方式,但仍然逐行读取(使用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")