如何在python中启动多个作业并与主作业进行通信

时间:2016-11-25 10:09:07

标签: python multithreading python-2.7 multiprocessing

我是python多线程/多处理的新手,所以请耐心等待。 我想解决以下问题,在这方面我需要一些帮助/建议。 让我简要介绍一下:

  1. 我想启动一个python脚本,它可以在其中执行某些操作 顺序开始。

  2. 顺序部分结束后,我想开始一些工作 并行。

    • 假设我想要启动四个并行作业。
    • 我还想在其他一些机器上使用" lsf"在计算集群上。我的初始脚本也在“lsf”上运行 机。
    • 我在四台机器上开始的四个工作将执行两个逻辑步骤A和B ---一个接一个。
    • 当作业最初开始时,他们从逻辑步骤A开始并完成它。
    • 每个工作(4jobs)完成步骤A后;他们应该通知开始这些的第一份工作。换句话说,开始的主要工作是等待这四个工作的确认。
    • 一旦主要工作收到这四个工作的确认;它应该通知所有四个作业以执行逻辑步骤B.
    • 完成任务后,逻辑步骤B将自动终止作业。
    • 主要工作是等待所有工作完成,之后应该继续顺序部分。
  3. 示例场景是:

    • 在集群中的“lsf”机器上运行的Python脚本启动四个" tcl shell"在四台“lsf”机器上。
    • 在每个tcl shell中,都会提供一个脚本来执行逻辑步骤A.
    • 完成步骤A后,他们应该以某种方式通知正在等待确认的python脚本。
    • 一旦收到所有四个人的确认,python脚本就会通知他们进行逻辑步骤B.
    • 逻辑步骤B也是一个源自其tcl shell的脚本;这个脚本也会在最后关闭tcl shell。
    • 同时,python脚本正在等待所有四个作业完成。
    • 完成所有四项工作后;它应该再次继续顺序部分,然后再完成。
  4. 以下是我的问题:

    1. 我很困惑---我应该使用多线程/多处理吗?哪一个更适合? 实际上这两者有什么区别?我读到了这些,但我无法得出结论。

    2. 什么是python GIL?我也在任何一个时间点读到某个地方只会执行一个线程。 我在这里需要一些解释。它让我觉得我无法使用线程。

    3. 关于如何系统地以更加pythonic的方式解决我的问题的任何建议。 我正在寻找一些口头的逐步解释和一些指针,以阅读每一步。 一旦概念清楚,我想自己编写代码。

    4. 提前致谢。

2 个答案:

答案 0 :(得分:0)

1)从您在问题中列出的选项中,您可能应该在这种情况下使用multiprocessing来利用多个CPU核心并并行计算。

2)从第1点开始:全局解释器锁(GIL)意味着任何时候只有一个线程可以实际执行代码。
在这里经常弹出的multithreading的简单示例是提示用户输入,例如,数学问题的答案。在后台,他们希望计时器以一秒的间隔保持递增,以记录该人响应的时间。如果没有多线程,程序将在等待用户输入时阻塞,并且计数器不会递增。在这种情况下,您可以让计数器和输入提示在不同的线程上运行,以便它们看起来同时运行。实际上,两个线程共享相同的CPU资源,并且不断向前和向后传递一个对象(GIL)以授予它们对CPU的单独访问权限。如果你想要并行地正确处理事情,这是没有希望的。 (注意:实际上,您只需记录提示之前和之后的时间并计算差异而不是打扰线程。)

3)我使用multiprocessing做了一个非常简单的例子。在这种情况下,我产生4个进程,计算随机选择范围的平方和。这些进程没有共享的GIL,因此不像multithreading那样独立执行。在此示例中,您可以看到所有进程在稍微不同的时间开始和结束,但我们可以将进程的结果聚合到单个queue对象中。在继续之前,父进程将等待所有4个子进程返回其计算。然后,您可以重复func_B的代码(不包含在代码中)。

import multiprocessing as mp
import time
import random
import sys

def func_A(process_number, queue):
    start = time.time()
    print "Process {} has started at {}".format(process_number, start)
    sys.stdout.flush()
    my_calc = sum([x**2 for x in xrange(random.randint(1000000, 3000000))])

    end = time.time()
    print "Process {} has ended at {}".format(process_number, end)
    sys.stdout.flush()
    queue.put((process_number, my_calc))

def multiproc_master():
    queue = mp.Queue()

    processes = [mp.Process(target=func_A, args=(x, queue)) for x in xrange(4)]
    for p in processes:
        p.start()

    # Unhash the below if you run on Linux (Windows and Linux treat multiprocessing
    # differently as Windows lacks os.fork())
    #for p in processes:
    #    p.join()

    results = [queue.get() for p in processes]
    return results

if __name__ == '__main__':
    split_jobs = multiproc_master()
    print split_jobs

答案 1 :(得分:0)

除了roganjosh的答案之外,我还会在A完成后包含一些信号来启动步骤B:

import multiprocessing as mp
import time
import random
import sys

def func_A(process_number, queue, proceed):
    print "Process {} has started been created".format(process_number)

    print "Process {} has ended step A".format(process_number)
    sys.stdout.flush()
    queue.put((process_number, "done"))

    proceed.wait() #wait for the signal to do the second part
    print "Process {} has ended step B".format(process_number)
    sys.stdout.flush()

def multiproc_master():
    queue = mp.Queue()
    proceed = mp.Event()

    processes = [mp.Process(target=func_A, args=(x, queue)) for x in range(4)]
    for p in processes:
        p.start()

    #block = True waits until there is something available
    results = [queue.get(block=True) for p in processes]
    proceed.set() #set continue-flag
    for p in processes: #wait for all to finish (also in windows)
        p.join()
    return results

if __name__ == '__main__':
    split_jobs = multiproc_master()
    print split_jobs