【问题标题】:Python subprocess Popen.terminate() still stuck on wait()Python 子进程 Popen.terminate() 仍然停留在 wait()
【发布时间】:2016-11-04 08:25:46
【问题描述】:

我正在尝试使用 RPi 实现无线电播放器。目标是设置播放列表并在填充播放列表后开始播放。一旦停止代码被执行,播放器和广播进程应该退出。

广播进程很好地终止,但播放器进程即使在调用终止后仍处于等待状态。如果再次调用停止代码,则播放器进程终止

尝试过的事情:

  1. 重新排序等待命令(播放器、收音机)/(收音机、播放器)
  2. 类似地重新排序终止命令
  3. 使用 kill 而不是 terminate 会挂起 RPi

玩家代码:

while playlist:
    player = subprocess.Popen(
            ["avconv", "-i", "Music/%s" % playlist[0], "-f", "s16le", "-ar", 
            "22.05k", "-ac", "1", "-"], stdout=subprocess.PIPE)

    radio = subprocess.Popen(["./pifm", "-", freq], stdin=player.stdout)

    radio.wait()
    print "************ exiting from radio :)"
    player.wait()
    print "************ exiting from player :)"

    playlist.pop(0)
player = None
radio = None

播放器停止代码(从另一个线程调用):

print "************ stop requested"

if radio and radio.poll() is None:
    print "************ terminating radio :)"
    radio.terminate()

if player and player.poll() is None:
    print "************ terminating player :)"
    player.terminate()

替代方案:

另一种选择是为广播设置一个持久接收器,并为播放器设置点播流程

def start_radio():
    global radio
    radio = subprocess.Popen(["./pifm"...], stdin=subprocess.PIPE)

def play():
    global player
    while playlist and radio:
        player = subprocess.Popen(["avconv"...], stdout=radio.stdin)
        player.wait()
        playlist.pop(0)

def stop():
   if player and player.poll() is None:
      print "************ terminating player :)"
      player.terminate()

但在这种情况下,调用 player.terminate() 会关闭播放器,同时在无线电进程上重复播放最后一个数据包(就像一个卡住的记录)。这张卡住的唱片一直播放到我启动新播放器或终止收音机为止。

【问题讨论】:

  • 试试player.join() 然后player.terminate()?
  • @ritlew player 是 Popen 对象,没有 join 方法。
  • 1- "使用 kill 而不是 terminate 会挂起 RPi" -- 你应该先解决这个问题(它也可能让你回答当前的问题)。 2-你应该在radio = Popen(...)之后调用player.stdout.close(),这样如果广播进程退出,播放器进程就会退出。
  • @J.F.Sebastian 它有效。请发布建议作为答案,我会接受。
  • @J.F.Sebastian 你能帮我看看替代部分吗?

标签: python raspberry-pi subprocess python-multithreading


【解决方案1】:

正如@J.F.Sebastian 提到的,使用player.stdout.close() 有效。整个代码库在这里发布https://github.com/hex007/pifm_server

所以最终的代码看起来像这样

while playlist:
    player = subprocess.Popen(
            ["avconv", "-i", "Music/%s" % playlist[0], "-f", "s16le", "-ar", 
            "22.05k", "-ac", "1", "-"], stdout=subprocess.PIPE)

    radio = subprocess.Popen(["./pifm", "-", freq], stdin=player.stdout)

    player.stdout.close()

    radio.wait()
    player.wait()

    if stop_requested:
        stop_requested = False
        break

    playlist.pop(0)

player = None
radio = None

以及停止代码:

stop_requested = True

if radio and radio.poll() is None:
    radio.terminate()

if player and player.poll() is None:
    player.terminate()

【讨论】:

  • 不相关:要在 Python 中实现 avconv ... | pifm ... 管道,您可以以相反的顺序启动程序(这不会影响结果——它们应该并行运行):pifm = Popen(['pifm', '...'], stdin=PIPE); player = Popen(['avconv', '...'], stdout=pifm.stdin); pifm.communicate(); player.wait() (在这种情况下,如果pifm 死了,那么即使您忘记关闭pifm.stdinavconv 也会知道)。请参阅@Cristian 对How do I use subprocess.Popen to connect multiple processes by pipes?的回答@
  • @J.F.Sebastian 替代部分的主要思想是为 pifm 提供一个接收器,因为如果 stdin 为空白,pifm 会传输静音;一个有利的特点。然后将管道连接到另一个转储到 pifm stdin ondemand 的进程。这种行为要求即使标准输入关闭也不会终止的持久无线电进程。我正在研究你提到的那篇文章。
  • 要明确 both 方法模拟 相同 shell 管道:avconv | pifm(如果您期望不同的结果,那么期望是错误的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多