多个shell子进程的非阻塞实时读取(Python)

时间:2018-02-05 08:51:24

标签: python multithreading asynchronous ffmpeg subprocess

我正在使用ffmpegsubrocess构建实时多视频流监控。 我目前拥有以下代码,受"Async and await with subprocesses" post启发。

问题是在一段时间后输出停止打印并且进程进入僵尸模式。我猜这个问题与PIPE的过载或死锁有关。需要帮助。

"""Async and await example using subprocesses

Note:
    Requires Python 3.6.
"""

import os
import sys
import time
import platform
import asyncio

async def run_command_shell(command):
    """Run command in subprocess (shell)

    Note:
        This can be used if you wish to execute e.g. "copy"
        on Windows, which can only be executed in the shell.
    """
    # Create subprocess
    process = await asyncio.create_subprocess_shell(
        command,
        stderr=asyncio.subprocess.PIPE)

    # Status
    print('Started:', command, '(pid = ' + str(process.pid) + ')')

    # Wait for the subprocess to finish
    stdout, stderr = await process.communicate()

    # Progress
    if process.returncode == 0:
        print('Done:', command, '(pid = ' + str(process.pid) + ')')
    else:
        print('Failed:', command, '(pid = ' + str(process.pid) + ')')

    # Result
    result = stderr.decode().strip()

    # Real time print
    print(result)

    # Return stdout
    return result


def make_chunks(l, n):
    """Yield successive n-sized chunks from l.

    Note:
        Taken from https://stackoverflow.com/a/312464
    """
    if sys.version_info.major == 2:
        for i in xrange(0, len(l), n):
            yield l[i:i + n]
    else:
        # Assume Python 3
        for i in range(0, len(l), n):
            yield l[i:i + n]


def run_asyncio_commands(tasks, max_concurrent_tasks=0):
    """Run tasks asynchronously using asyncio and return results

    If max_concurrent_tasks are set to 0, no limit is applied.

    Note:
        By default, Windows uses SelectorEventLoop, which does not support
        subprocesses. Therefore ProactorEventLoop is used on Windows.
        https://docs.python.org/3/library/asyncio-eventloops.html#windows
    """

    all_results = []

    if max_concurrent_tasks == 0:
        chunks = [tasks]
    else:
        chunks = make_chunks(l=tasks, n=max_concurrent_tasks)

    for tasks_in_chunk in chunks:
        if platform.system() == 'Windows':
            loop = asyncio.ProactorEventLoop()
            asyncio.set_event_loop(loop)
        else:
            loop = asyncio.get_event_loop()

        commands = asyncio.gather(*tasks_in_chunk)  # Unpack list using *
        results = loop.run_until_complete(commands)
        all_results += results
        loop.close()
    return all_results


if __name__ == '__main__':

    start = time.time()

    if platform.system() == 'Windows':
        # Commands to be executed on Windows
        commands = [
            ['hostname']
        ]
    else:
        # Commands to be executed on Unix
        commands = [
            ['du', '-sh', '/var/tmp'],
            ['hostname'],
        ]
    cmds = [["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx  -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"],
            ["ffmpeg -y -i udp://xxx.xx.xx.xxx:xxxx -f null -"]]

    tasks = []
    for command in cmds:
        tasks.append(run_command_shell(*command))


    # # Shell execution example
    # tasks = [run_command_shell('copy c:/somefile d:/new_file')]

    # # List comprehension example
    # tasks = [
    #     run_command(*command, get_project_path(project))
    #     for project in accessible_projects(all_projects)
    # ]

    results = run_asyncio_commands(tasks, max_concurrent_tasks=20)  # At most 20 parallel tasks
    print('Results:', results)

    end = time.time()
    rounded_end = ('{0:.4f}'.format(round(end-start,4)))
    print('Script ran in about', str(rounded_end), 'seconds')

相关:Non-blocking read from multiple subprocesses (Python)

1 个答案:

答案 0 :(得分:0)

事实证明,问题可能与多线程 asyncio 等的代码优化无关

原因可能是服务器限制,例如打开文件/文件描述符(FD)的最大数量,防火墙,其他配置文件。

如果你偶然发现了类似的问题:

安装htop

Htop是一个用于Linux / Unix系统的交互式实时进程监控应用程序,也是top命令的便捷替代方案,它是在所有Linux上预装的默认进程监视工具操作系统。

这可能有助于澄清原因。

测试单个ffmpeg命令

正如jfs所说,我需要Minimal, Complete, and Verifiable example。所以我们从一个非常小的开始:测试一个过程。

ffmpeg -y -i udp://224.10.0.123:1234  -f null -
  

在我的情况下,事实证明任何多播将在2:10 - 2:20挂起   分钟。处于活动状态但处于僵尸模式。这很奇怪,   因为几天前一切都很完美。

测试另一个软(VLC's multicat

Multicat的最新官方版本编号为2.2,可用here

获取它并且不要忘记,biTStream需要在构建时安装。

使用命令检查流以记录流中的视频:

timeout 10 multicat -uU @224.10.0.123:1234 test.ts
  

在我的情况下,同样的事情发生在第二分钟。该命令不会停止执行,但文件已停止记录。

检查打开的文件/文件描述符的最大数量 more info

使用以下命令命令显示最大打开文件描述符数:

cat /proc/sys/fs/file-max

要查看硬值和软值,请按以下步骤发出命令:

ulimit -Hn
ulimit -Sn
  

在执行我的一个python脚本的某个时刻,我看到了类似的错误,但是这个参数的增加并没有帮助我。

摘要

所以问题与我脚本的并行执行无关。另一台虚拟机的验证成功。我联系了设置这个虚拟机的人并向他解释过去几天发生了什么事情,这表明问题出在防火墙上。他说他没有触及任何东西。但在此之后,一切都开始完美。 (我几乎可以肯定他打破了它):D

GL大家!