任务的Python并行化(事件循环?)

时间:2013-04-09 07:42:51

标签: python parallel-processing subprocess twisted gevent

现状

我正在写a small Python program来播放终端中8tracks的播放列表。

它由三部分组成:client.py使用stdlib的cmd模块,api.py模块使用python请求访问API,player.py模块使用在从模式下创建一个mplayer子进程并向其发送命令。

问题

到目前为止,问题在于除了轮询子进程'stdout之外,我没有别的办法可以判断一首歌是否已经在mplayer中播放完了。这意味着我必须观看这个过​​程,以便在歌曲播放完毕后我可以请求并开始播放列表中的下一首歌曲。

问题是等待子进程会阻塞cmd模块的主循环。我不能简单地在单独的线程或进程中运行它,因为我必须共享对子进程'stdout的引用,并且这些引用不能在进程之间共享。

可能的方法

我想到了不同的解决方案。我可以将player.py放在一个单独的进程中并通过队列发送文本命令,但这会使事情过于复杂。我可以创建一个Twisted应用程序,但Twisted非常大,我不知道从哪里开始。另外,我不希望在我的项目中有这样的依赖。

第三种解决方案是使用Gevent。问题是我如何使用cmd模块。据我所知,Gevent,我必须在每个我“等待”某事的地方屈服。在这种情况下,这将是在HTTP请求期间,在cmd.cmdloop()期间等待以及在子进程轮询之间的暂停期间。但是如何让cmd模块产生?某种子类或猴子修补?

2 个答案:

答案 0 :(得分:1)

从代码的外观来看,你可以在与mplayer通信时使用pexpect。 Pexpect(或者对此事的期待)非常适合来回传播。

答案 1 :(得分:-2)

似乎我明白了。我以前尝试使用线程的所有尝试都使用multiprocessing.dummy模块,该模块包装了threading模块,但在传递参数时行为略有不同 - 您无法传递引用。

直接使用threading,它似乎有效。我是通过每次加载新曲目时启动后台线程来实现的。当曲目播放完毕后,我向客户端发送SIGUSR1信号,通过加载和播放新歌来处理它。

player.py

import os
import signal
import threading
from pipes import quote

class MPlayer(object):

    def __init__(self):
        self.process = Process(['mplayer',
            '-slave', '-idle',
            '-really-quiet', '-msglevel', 'global=6:cplayer=4', '-msgmodule',
            '-input', 'nodefault-bindings',
            '-cache', '1024',
        ], bufsize=1)
        self.write_lock = threading.Lock()

    # (...)

    def load(self, path):

        with self.write_lock:
            self.p.write('loadfile {}\n'.format(quote(path)))

        def wait_for_finish(process):
            # HERE: poll process for track ending with process.read()
            os.kill(os.getpid(), signal.SIGUSR1)

        t = threading.Thread(target=wait_for_finish, args=(self.process,))
        t.daemon = True
        t.start()

client.py

import cmd
import signal
from player import MPlayer

class PlayCommand(cmd.Cmd, object):

    def __init__(self, *args, **kwargs):

        # (...)

        self.p = MPlayer()
        signal.signal(signal.SIGUSR1, self._song_end_handler)

    def _song_end_handler(self, signum, frame):
        print('SIGUSR1!!!!!!!!!!111!1')
        # HERE: Fetch new track URL
        self.p.load()

但是,如果有人认为他/她已找到使用协同程序或事件的更好解决方案,请随时回答您的解决方案。