Multiprocessing Queue, Pool 및 Locking을 사용하는 단순한 예


91

http://docs.python.org/dev/library/multiprocessing.html 에서 설명서를 읽으려고했지만 여전히 다중 처리 대기열, 풀 및 잠금으로 어려움을 겪고 있습니다. 그리고 지금은 아래 예제를 만들 수있었습니다.

Queue와 Pool의 경우 개념을 제대로 이해했는지 잘 모르겠습니다. 틀 렸으면 바로 수정 해주세요. 내가 달성하려는 것은 한 번에 2 개의 요청을 처리하는 것입니다 (이 예에서는 데이터 목록에 8 개 있음). 그래서 무엇을 사용해야합니까? 두 개의 서로 다른 대기열 (최대 2 개)을 처리 할 수있는 2 개의 프로세스를 생성하기위한 풀 또는 대기열을 사용하여 매번 2 개의 입력을 처리해야합니까? 잠금은 출력을 올바르게 인쇄하는 것입니다.

import multiprocessing
import time

data = (['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
        ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)


def mp_handler(var1):
    for indata in var1:
        p = multiprocessing.Process(target=mp_worker, args=(indata[0], indata[1]))
        p.start()


def mp_worker(inputs, the_time):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

if __name__ == '__main__':
    mp_handler(data)

답변:


129

문제에 대한 최선의 해결책은 Pool. Queues를 사용 하고 별도의 "대기열 공급"기능을 갖는 것은 아마도 과잉 일 것입니다.

여기에 프로그램의 약간 재 배열 버전,이 시간 만이 프로세스 A의 coralled Pool. 원본 코드를 최소한으로 변경하는 것이 가장 쉬운 방법이라고 생각합니다.

import multiprocessing
import time

data = (
    ['a', '2'], ['b', '4'], ['c', '6'], ['d', '8'],
    ['e', '1'], ['f', '3'], ['g', '5'], ['h', '7']
)

def mp_worker((inputs, the_time)):
    print " Processs %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs

def mp_handler():
    p = multiprocessing.Pool(2)
    p.map(mp_worker, data)

if __name__ == '__main__':
    mp_handler()

하는 것으로 mp_worker()기능은 이제 때문에 하나의 인자 (앞의 두 인수의 튜플을) 받아map() 하위 목록에 사용자의 입력 데이터, 당신의 노동자 함수에 하나의 인수로 주어진 각각의 하위리스트 업 기능의 덩어리.

산출:

Processs a  Waiting 2 seconds
Processs b  Waiting 4 seconds
Process a   DONE
Processs c  Waiting 6 seconds
Process b   DONE
Processs d  Waiting 8 seconds
Process c   DONE
Processs e  Waiting 1 seconds
Process e   DONE
Processs f  Waiting 3 seconds
Process d   DONE
Processs g  Waiting 5 seconds
Process f   DONE
Processs h  Waiting 7 seconds
Process g   DONE
Process h   DONE

아래 @Thales 의견에 따라 편집하십시오.

프로세스가 직렬 쌍으로 실행되도록 "각 풀 제한에 대한 잠금"을 원하는 경우 ala :

A 대기 B 대기 | A 완료, B 완료 | C 대기, D 대기 | C 완료, D 완료 | ...

그런 다음 핸들러 함수를 변경하여 각 데이터 쌍에 대해 풀 (2 개 프로세스)을 시작합니다.

def mp_handler():
    subdata = zip(data[0::2], data[1::2])
    for task1, task2 in subdata:
        p = multiprocessing.Pool(2)
        p.map(mp_worker, (task1, task2))

이제 출력은 다음과 같습니다.

 Processs a Waiting 2 seconds
 Processs b Waiting 4 seconds
 Process a  DONE
 Process b  DONE
 Processs c Waiting 6 seconds
 Processs d Waiting 8 seconds
 Process c  DONE
 Process d  DONE
 Processs e Waiting 1 seconds
 Processs f Waiting 3 seconds
 Process e  DONE
 Process f  DONE
 Processs g Waiting 5 seconds
 Processs h Waiting 7 seconds
 Process g  DONE
 Process h  DONE

이를 수행하는 방법에 대한 간단하고 직접적인 예를 들어 주셔서 감사합니다. 그러나 각 풀 제한에 대해 잠금을 어떻게 적용 할 수 있습니까? 난 당신이 코드를 실행하면, 내가 "| A는, B 수행 완료 | | C 대기, D 대기 C 수행, D 수행 B 대기 대기 A"같은 것을보고 싶다, 의미
thclpr

2
즉, A와 B가 모두 완료 될 때까지 C가 시작되는 것을 원하지 않습니까?
Velimir Mlaker

정확히, 나는 multiprocessing.Process를 사용하여 할 수 있지만 풀을 사용하여 어떻게 할 수 있는지 알 수 없습니다
thclpr

감사합니다. 의도 한대로 작업하지만 mp_handler 함수에서 var1 대신 변수 데이터를 참조하고 있습니다. :)
thclpr

좋아요, 저는 var1모두 제거 하고 data대신 글로벌을 참조했습니다 .
Velimir Mlaker

8

이것은 질문과 100 % 관련이 없을 수 있지만 대기열과 함께 다중 처리를 사용하는 예를 검색하면 Google에서 가장 먼저 표시됩니다.

이것은 항목을 인스턴스화하여 큐에 넣을 수 있으며 큐가 완료 될 때까지 기다릴 수있는 기본 예제 클래스입니다. 그게 내가 필요한 전부입니다.

from multiprocessing import JoinableQueue
from multiprocessing.context import Process


class Renderer:
    queue = None

    def __init__(self, nb_workers=2):
        self.queue = JoinableQueue()
        self.processes = [Process(target=self.upload) for i in range(nb_workers)]
        for p in self.processes:
            p.start()

    def render(self, item):
        self.queue.put(item)

    def upload(self):
        while True:
            item = self.queue.get()
            if item is None:
                break

            # process your item here

            self.queue.task_done()

    def terminate(self):
        """ wait until queue is empty and terminate processes """
        self.queue.join()
        for p in self.processes:
            p.terminate()

r = Renderer()
r.render(item1)
r.render(item2)
r.terminate()

2
무엇 item1item2? 두 개의 다른 프로세스에서 실행되는 일종의 작업 또는 기능입니까?
Zelphir Kaltstahl

2
예, 병렬 방식으로 처리되는 작업 또는 입력 매개 변수입니다.
linqu 2011

8

이 주제에 대한 나의 개인적인 이동은 다음과 같습니다.

요점, (풀 요청 환영!) : https://gist.github.com/thorsummoner/b5b1dfcff7e7fdd334ec

import multiprocessing
import sys

THREADS = 3

# Used to prevent multiple threads from mixing thier output
GLOBALLOCK = multiprocessing.Lock()


def func_worker(args):
    """This function will be called by each thread.
    This function can not be a class method.
    """
    # Expand list of args into named args.
    str1, str2 = args
    del args

    # Work
    # ...



    # Serial-only Portion
    GLOBALLOCK.acquire()
    print(str1)
    print(str2)
    GLOBALLOCK.release()


def main(argp=None):
    """Multiprocessing Spawn Example
    """
    # Create the number of threads you want
    pool = multiprocessing.Pool(THREADS)

    # Define two jobs, each with two args.
    func_args = [
        ('Hello', 'World',), 
        ('Goodbye', 'World',), 
    ]


    try:
        # Spawn up to 9999999 jobs, I think this is the maximum possible.
        # I do not know what happens if you exceed this.
        pool.map_async(func_worker, func_args).get(9999999)
    except KeyboardInterrupt:
        # Allow ^C to interrupt from any thread.
        sys.stdout.write('\033[0m')
        sys.stdout.write('User Interupt\n')
    pool.close()

if __name__ == '__main__':
    main()

1
.map_async ()가 어떤 식 으로든 .map ()보다 낫다는 것은 확실하지 않습니다.
ThorSummoner 2010 년

3
에 대한 인수 get()는 시간 초과이며 시작된 작업 수와 관련이 없습니다.
mata

@mata 그래서 그것은 폴링 루프에서 사용된다는 의미입니까? .get(timeout=1)? .get()완성 된 목록을 가져 오라고 말해도 됩니까?
ThorSummoner

예, .get()모든 결과를 사용할 수있을 때까지 무기한 대기하고 결과 목록을 반환합니다. 폴링 루프를 사용하여 날씨 결과를 사용할 수 있는지 확인하거나 map_async()호출 에서 콜백 함수를 전달할 수 있습니다. 그러면 모든 결과가 사용 가능 해지면 호출됩니다.
mata

2

Komodo Edit (win10)와 같은 편집기를 사용하는 모든 사람을 sys.stdout.flush()위해 다음을 추가하십시오 .

def mp_worker((inputs, the_time)):
    print " Process %s\tWaiting %s seconds" % (inputs, the_time)
    time.sleep(int(the_time))
    print " Process %s\tDONE" % inputs
    sys.stdout.flush()

또는 첫 번째 줄로 :

    if __name__ == '__main__':
       sys.stdout.flush()

이것은 스크립트를 실행하는 동안 무슨 일이 일어나는지 보는 데 도움이됩니다. 검은 색 명령 줄 상자를 볼 필요가 없습니다.


1

다음은 내 코드의 예입니다 (스레드 풀의 경우 클래스 이름 만 변경하면 프로세스 풀이 생김).

def execute_run(rp): 
   ... do something 

pool = ThreadPoolExecutor(6)
for mat in TESTED_MATERIAL:
    for en in TESTED_ENERGIES:
        for ecut in TESTED_E_CUT:
            rp = RunParams(
                simulations, DEST_DIR,
                PARTICLE, mat, 960, 0.125, ecut, en
            )
            pool.submit(execute_run, rp)
pool.join()

원래:

  • pool = ThreadPoolExecutor(6) 6 개의 스레드에 대한 풀을 만듭니다.
  • 그런 다음 풀에 작업을 추가하는 for가 많이 있습니다.
  • pool.submit(execute_run, rp) 풀에 작업을 추가하고 첫 번째 arogument는 스레드 / 프로세스에서 호출 된 함수이고 나머지 인수는 호출 된 함수로 전달됩니다.
  • pool.join 모든 작업이 완료 될 때까지 기다립니다.

2
을 사용 concurrent.futures하고 있지만 OP는 multiprocessingPython 2.7 에 대해 묻습니다 .
Tim Peters
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.