从一个脚本运行多个python脚本,并在它们之间来回通信?

时间:2018-11-22 05:42:12

标签: python multithreading parallel-processing

我写了一个脚本,能够将参数传递给我,我想使用唯一的参数启动多个同时迭代(可能是100多个)。我的计划是编写另一个python脚本,然后启动这些下标/进程,但是要有效,我需要该脚本能够监视下标是否有错误。

是否有任何简单的方法或提供此功能的库?我已经搜寻了一段时间,但没有找到任何好运。创建子进程和多个线程似乎很简单,但是我找不到关于如何与这些线程/子进程进行通信的指南或教程。

3 个答案:

答案 0 :(得分:0)

一个更好的方法是利用线程。如果在较大的脚本中创建了要调用的脚本,则可以让主函数根据需要多次调用此脚本,并使线程根据需要报告信息。您可以阅读有关here线程如何工作的一些知识。

答案 1 :(得分:0)

尽管有必要,我还是建议使用threading.Threadmultiprocessing.Process

在线程/进程之间进行通信的简单方法是使用Queue。多处理模块提供了其他一些在进程之间进行通信的方式(队列,事件,管理器等)

您可以在示例中看到一些基本信息:

import threading
from Queue import Queue
import random
import time


class Worker(threading.Thread):
    def __init__(self, name, queue_error):
        threading.Thread.__init__(self)
        self.name = name
        self.queue_error = queue_error

    def run(self):
        time.sleep(random.randrange(1, 10))
        # Do some processing ...
        # Report errors
        self.queue_error.put((self.name, 'Error state'))


class Launcher(object):
    def __init__(self):
        self.queue_error = Queue()

    def main_loop(self):
        # Start threads
        for i in range(10):
            w = Worker(i, self.queue_error)
            w.start()
        # Check for errors
        while True:
            while not self.queue_error.empty():
                error_data = self.queue_error.get()
                print 'Worker #%s reported error: %s' % (error_data[0], error_data[1])
            time.sleep(0.1)


if __name__ == '__main__':
    l = Launcher()
    l.main_loop()

答案 2 :(得分:0)

就像其他人所说的那样,您必须使用多个进程来实现真正的并行性而不是线程,因为GIL限制会阻止线程并发运行。

如果要使用标准的 multiprocessing 库(基于启动多个进程),建议使用pool of workers。如果我理解正确,则要启动100多个并行实例。在一台主机上启动100多个进程将产生过多的开销。而是创建一个由P个工作程序组成的池,其中P是例如计算机中的核心数,然后将100多个作业提交给该池。这很容易做到,网络上有很多examples。另外,将作业提交到池中时,可以提供回调函数来接收错误。这可能足以满足您的需求(有示例here)。

但是,上一次我在多处理池中无法在多个主机(例如计算机集群)之间分配工作。因此,如果您需要执行此操作,或者需要一个更灵活的通信方案,例如能够在工作人员运行时向控制过程发送更新,那么我的建议是使用charm4py(请注意,一个charm4py开发人员,所以这是我的经验。

使用charm4py,您可以创建N个工作程序,这些工作程序由运行时分布在P个进程之间(跨多个主机工作),并且这些工作程序只需执行远程方法调用即可与控制器进行通信。这是一个小例子:

from charm4py import charm, Chare, Group, Array, ArrayMap, Reducer, threaded
import time

WORKER_ITERATIONS = 100


class Worker(Chare):

    def __init__(self, controller):
        self.controller = controller

    @threaded
    def work(self, x, done_future):
        result = -1
        try:
            for i in range(WORKER_ITERATIONS):
                if i % 20 == 0:
                    # send status update to controller
                    self.controller.progressUpdate(self.thisIndex, i, ret=True).get()
                if i == 5 and self.thisIndex[0] % 2 == 0:
                    # trigger NameError on even-numbered workers
                    test[3] = 3
                time.sleep(0.01)
            result = x**2
        except Exception as e:
            # send error to controller
            self.controller.collectError(self.thisIndex, e)
        # send result to controller
        self.contribute(result, Reducer.gather, done_future)


# This custom map is used to prevent workers from being created on process 0
# (where the controller is). Not strictly needed, but allows more timely
# controller output
class WorkerMap(ArrayMap):
    def procNum(self, index):
        return (index[0] % (charm.numPes() - 1)) + 1


class Controller(Chare):

    def __init__(self, args):
        self.startTime = time.time()
        done_future = charm.createFuture()
        # create 12 workers, which are distributed by charm4py among processes
        workers = Array(Worker, 12, args=[self.thisProxy], map=Group(WorkerMap))
        # start work
        for i in range(12):
            workers[i].work(i, done_future)
        print('Results are', done_future.get())  # wait for result
        exit()

    def progressUpdate(self, worker_id, current_step):
        print(round(time.time() - self.startTime, 3), ': Worker', worker_id,
              'progress', current_step * 100 / WORKER_ITERATIONS, '%')
        # the controller can return a value here and the worker would receive it

    def collectError(self, worker_id, error):
        print(round(time.time() - self.startTime, 3), ': Got error', error,
              'from worker', worker_id)


charm.start(Controller)

在此示例中,控制器将在状态更新和错误发生时打印它们。它 完成后将打印所有工作人员的最终结果。工人的成果 失败的将是-1。

启动时给出进程数P。运行时将在可用进程之间分配N个工作程序。在创建工作程序时会发生这种情况,在此特定示例中没有动态负载平衡。

另外,请注意,在charm4py模型中,远程方法调用是异步的,并返回调用者可以阻止的将来,但只有调用线程阻止(而不是整个过程)。

希望这会有所帮助。