python input()具有多个进程的块

时间:2018-04-04 14:34:50

标签: python python-multiprocessing

我正在尝试使用多处理来执行多个后台作业,并使用主进程作为用户界面,通过input()接受命令。每个进程都必须执行某些作业,并将其当前状态写入字典,该字典是使用manager.dict()创建的,然后传递给进程。

创建进程后,有一个带input()的循环用于访问用户命令。为简单起见,这些命令减少到最低限度。

from multiprocessing import Manager
from multiprocessing import Process
with Manager() as manager:
    producers = []
    settings = [{'name':'test'}]

    for setting in settings:
        status = manager.dict()
        logger.info("Start Producer {0}".format(setting['name']))
        producer = Process(target=start_producer, args=(setting, status))
        producer.start()
        producers.append([producer, status])

    logger.info("initialized {0} producers".format(len(producers)))

    while True:
        text_command = input('Enter your command:')
        if text_command == 'exit':
            logger.info("waiting for producers")
            for p in producers:
                p[0].join()
            logger.info("Exit application.")
            break
        elif text_command == 'status':
            for p in producers:
                if 'name' in p[1] and 'status' in p[1]:
                    print('{0}:{1}'.format(p[1]['name'], p[1]['status']))
        else:
            print("Unknown command.")

在其他过程中运行的方法非常简单:

def start_producer(producer_setting: dict, status_dict: dict):
    importer = MyProducer(producer_setting)
    importer.set_status_dict(status_dict)
    importer.run()

我创建一个MyProducer实例并通过对象的setter设置status-dictionary并调用阻塞run()方法,该方法仅在生产者完成时返回。在致电set_status_dict(status_dict)时,字典中会填充namestatus元素。

当我运行代码时,生产者似乎被创建,我收到“Start Producer test”和“initialized 1 producer”输出,之后是input()的“输入你的命令”请求,但是似乎实际过程没有运行。

当我按Enter跳过第一次循环迭代时,我得到预期的“未知命令”日志,生产者进程开始实际工作。之后,我的“状态”命令也按预期工作。

当我在第一次循环迭代中输入'status'时,我得到一个键 - 错误,因为'name'和'status'没有在字典中设置。这些密钥应在set_status_dict()中设置,Process(target=...)本身在start_producer中调用。

为什么? producer.start()不应该在新进程中运行input()的完整块,因此永远不会挂在主进程的input()上吗?

如何在没有任何用户输入的情况下首先启动流程,然后等待sleep(1)

修改:有关此问题的完整mvce程序可在此处找到:https://pastebin.com/k8xvhLhn

编辑:找到初始化进程后start_producer()的解决方案。但为什么这种行为首先发生?不应该在新进程中运行--start-maximized中的所有代码吗?

1 个答案:

答案 0 :(得分:1)

我对多处理模块的经验有限,但我能够让它按照你想要的方式行事(我认为)。首先,我在while循环的顶部添加了一些print语句,以查看可能发生的情况,并发现如果进程为runjoin,则表明它有效。我认为你不希望它阻止,所以我添加了调用以进一步运行该过程 - 但似乎run()也阻止了。事实证明,当第一次while循环迭代出现时,该过程还没有完成 - 在循环顶部添加time.sleep(30)使得进程有足够的时间来调度(由OS)并运行。 (在我的机器上,实际上只需要200到300毫秒的午睡时间)

我将start_producer替换为:

def start_producer(producer_setting: dict, status_dict: dict):
##    importer = MyProducer(producer_setting)
##    importer.set_status_dict(status_dict)
##    importer.run()
    #time.sleep(30)
    status_dict['name'] = 'foo'
    status_dict['status'] = 'thinking'

您的代码已修改:

if __name__ == '__main__':
    with Manager() as manager:
        producers = []
        settings = [{'name':'test'}]

        for setting in settings:
            status = manager.dict()
            logger.info("Start Producer {0}".format(setting['name']))
            producer = Process(target=start_producer, args=(setting, status))
            producer.start()
            # add a call to run() but it blocks
            #producer.run()
            producers.append([producer, status])

        logger.info("initialized {0} producers".format(len(producers)))

        while True:
            time.sleep(30)
            for p, s in producers:
                #p.join()
                #p.run()
                print(f'name:{p.name}|alive:{p.is_alive()}|{s}')
                if 'name' in s and 'status' in s:
                    print('{0}:{1}'.format(s['name'], s['status']))
            text_command = input('Enter your command:')
            if text_command == 'exit':
                logger.info("waiting for producers")
                for p in producers:
                    p[0].join()
                logger.info("Exit application.")
                break
            elif text_command == 'status':
                for p in producers:
                    if 'name' in p[1] and 'status' in p[1]:
                        print('{0}:{1}'.format(p[1]['name'], p[1]['status']))
            else:
                print("Unknown command.")