멀티 프로세싱 풀과 유사한 스레딩 풀?


347

멀티 프로세싱 모듈의 풀 클래스 와 비슷한 워커 스레드에 대한 풀 클래스가 있습니까?

예를 들어지도 기능을 병렬화하는 쉬운 방법이 마음에 듭니다.

def long_running_func(p):
    c_func_no_gil(p)

p = multiprocessing.Pool(4)
xs = p.map(long_running_func, range(100))

그러나 나는 새로운 프로세스를 만드는 오버 헤드없이 그것을하고 싶습니다.

나는 길에 대해 알고있다. 그러나 유스 케이스에서 함수는 파이썬 랩퍼가 실제 함수 호출 전에 GIL을 해제하는 IO 바운드 C 함수입니다.

내 스레딩 풀을 작성해야합니까?



1
요즘에는 내장되어 from multiprocessing.pool import ThreadPool있습니다.
martineau

이것에 대해 자세히 설명해 주 I know about the GIL. However, in my usecase, the function will be an IO-bound C function for which the python wrapper will release the GIL before the actual function call.시겠습니까?
mrgloom 2016 년

답변:


448

난 그냥 사실이 있음을 발견 하다 에서 스레드 기반의 풀 인터페이스 multiprocessing그러나 그것은 다소 숨겨져 제대로 문서화되지, 모듈.

통해 가져올 수 있습니다

from multiprocessing.pool import ThreadPool

파이썬 스레드를 래핑하는 더미 프로세스 클래스를 사용하여 구현됩니다. 이 스레드 기반 프로세스 클래스는 docsmultiprocessing.dummy 에서 간단히 언급 할 수 있습니다 . 이 더미 모듈은 아마도 스레드를 기반으로 한 전체 멀티 프로세싱 인터페이스를 제공합니다.


5
대단해. 메인 스레드 외부에서 ThreadPools를 만드는 데 문제가 있었지만 일단 생성 된 하위 스레드에서 사용할 수 있습니다. 문제가 있습니다 : bugs.python.org/issue10015
Olson

82
이 클래스에 문서가없는 이유는 알 수 없습니다. 이러한 도우미 클래스는 오늘날 매우 중요합니다.
Wernight

18
@Wernight : 문서화 및 테스트를 포함하여 threading.ThreadPool과 같은 패치를 제공하는 패치를 제공 한 사람이 없기 때문에 주로 공개되지 않습니다. 실제로 표준 라이브러리에 포함시키는 것이 좋은 배터리 일 것입니다. 그러나 아무도 쓰지 않으면 일어날 수 없습니다. 멀티 프로세싱에서이 기존 구현의 한 가지 장점은 이러한 스레딩 패치를 훨씬 쉽게 작성할 수 있어야한다는 것입니다 ( docs.python.org/devguide )
ncoghlan

3
@ daniel.gindi : multiprocessing.dummy.Pool/ multiprocessing.pool.ThreadPool는 같은 것이며 스레드 풀입니다. 프로세스 풀 의 인터페이스 를 모방 하지만 스레딩 측면에서 완전히 구현됩니다. 문서를 다시 읽으면 거꾸로 얻습니다.
ShadowRanger 2016 년

9
@ daniel.gindi : 더 읽어보기 : " multiprocessing.dummyAPI를 복제 multiprocessing하지만 threading모듈을 감싸는 래퍼에 지나지 않습니다 ." multiprocessing일반적으로 프로세스에 관한 것이지만 프로세스와 스레드 간 전환을 허용하기 위해 (대부분) multiprocessingAPI를 복제 multiprocessing.dummy했지만 프로세스가 아닌 스레드로 백업했습니다. 목표는 import multiprocessing.dummy as multiprocessing프로세스 기반 코드를 스레드 기반으로 변경하는 것입니다.
ShadowRanger

236

Python 3에서는을 사용할 수 있습니다 concurrent.futures.ThreadPoolExecutor.

executor = ThreadPoolExecutor(max_workers=10)
a = executor.submit(my_function)

자세한 내용 과 예제 는 문서 를 참조하십시오 .


6
백 포트 된 선물 모듈을 사용하려면sudo pip install futures
yair

멀티 프로세싱을위한 가장 효율적이고 빠른 방법입니다
Haritsinh Gohil

2
사용 ThreadPoolExecutor과 의 차이점은 무엇입니까 multiprocessing.dummy.Pool?
Jay

63

그렇습니다. 동일한 API를 가진 것으로 보입니다.

import multiprocessing

def worker(lnk):
    ....    
def start_process():
    .....
....

if(PROCESS):
    pool = multiprocessing.Pool(processes=POOL_SIZE, initializer=start_process)
else:
    pool = multiprocessing.pool.ThreadPool(processes=POOL_SIZE, 
                                           initializer=start_process)

pool.map(worker, inputs)
....

9
에 대한 가져 오기 경로 ThreadPool가와 다릅니다 Pool. 올바른 수입은 from multiprocessing.pool import ThreadPool입니다.
Marigold

2
이상하게도 이것은 문서화 된 API가 아니며 multiprocessing.pool은 AsyncResult를 제공하는 것으로 간단히 언급됩니다. 그러나 2.x 및 3.x에서 사용할 수 있습니다.
Marvin

2
이것이 내가 찾던 것입니다. 단 하나의 가져 오기 라인과 기존 풀 라인을 약간만 변경하면 완벽하게 작동합니다.
Danegraphics

39

매우 간단하고 가벼운 무언가 ( 여기서 약간 수정 됨 ) :

from Queue import Queue
from threading import Thread


class Worker(Thread):
    """Thread executing tasks from a given tasks queue"""
    def __init__(self, tasks):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon = True
        self.start()

    def run(self):
        while True:
            func, args, kargs = self.tasks.get()
            try:
                func(*args, **kargs)
            except Exception, e:
                print e
            finally:
                self.tasks.task_done()


class ThreadPool:
    """Pool of threads consuming tasks from a queue"""
    def __init__(self, num_threads):
        self.tasks = Queue(num_threads)
        for _ in range(num_threads):
            Worker(self.tasks)

    def add_task(self, func, *args, **kargs):
        """Add a task to the queue"""
        self.tasks.put((func, args, kargs))

    def wait_completion(self):
        """Wait for completion of all the tasks in the queue"""
        self.tasks.join()

if __name__ == '__main__':
    from random import randrange
    from time import sleep

    delays = [randrange(1, 10) for i in range(100)]

    def wait_delay(d):
        print 'sleeping for (%d)sec' % d
        sleep(d)

    pool = ThreadPool(20)

    for i, d in enumerate(delays):
        pool.add_task(wait_delay, d)

    pool.wait_completion()

작업 완료시 콜백을 지원하려면 작업 튜플에 콜백을 추가하면됩니다.


무조건 무한 루프 인 경우 스레드는 어떻게 결합 할 수 있습니까?
Joseph Garvin

@JosephGarvin 나는 그것을 테스트했고, Queue.get()프로그램이 끝날 때까지 스레드가 빈 큐 ( 블로킹 하기 때문에 )가 계속 차단되고 나면 자동으로 종료됩니다.
forumulator

@JosephGarvin, 좋은 질문입니다. Queue.join()실제로 작업자 스레드가 아닌 작업 대기열에 참여합니다 . 따라서 큐가 비어 있으면 wait_completion리턴되고 프로그램이 종료되며 OS가 스레드를 가져옵니다.
randomir

이 코드가 모두 깔끔한 함수로 싸여 있으면 큐가 비어 있고 pool.wait_completion()리턴 되어도 스레드를 중지하지 않는 것 같습니다 . 결과적으로 스레드는 계속 빌드됩니다.
ubiquibacon 2018 년

17

안녕하세요 파이썬에서 스레드 풀을 사용하려면이 라이브러리를 사용할 수 있습니다 :

from multiprocessing.dummy import Pool as ThreadPool

그리고이 라이브러리를 사용하려면 다음과 같이하십시오.

pool = ThreadPool(threads)
results = pool.map(service, tasks)
pool.close()
pool.join()
return results

스레드는 원하는 스레드 수이며 작업은 대부분 서비스에 매핑되는 작업 목록입니다.


고마워, 그것은 좋은 제안입니다! 문서에서 : multiprocessing.dummy는 멀티 프로세싱 API를 복제하지만 스레딩 모듈을 감싸는 래퍼에 지나지 않습니다. 한 보정 - 난 당신이 풀 API는 (기능, 반복 가능)이라고 말하고 싶은 생각
layser가

2
우리는 .close().join()호출을 놓쳤으며 .map()모든 스레드가 완료되기 전에 완료됩니다. 경고 일뿐입니다.
Anatoly Scherbakov

8

마지막으로 사용한 결과는 다음과 같습니다. 위의 dgorissen에 의해 수정 된 클래스 버전입니다.

파일: threadpool.py

from queue import Queue, Empty
import threading
from threading import Thread


class Worker(Thread):
    _TIMEOUT = 2
    """ Thread executing tasks from a given tasks queue. Thread is signalable, 
        to exit
    """
    def __init__(self, tasks, th_num):
        Thread.__init__(self)
        self.tasks = tasks
        self.daemon, self.th_num = True, th_num
        self.done = threading.Event()
        self.start()

    def run(self):       
        while not self.done.is_set():
            try:
                func, args, kwargs = self.tasks.get(block=True,
                                                   timeout=self._TIMEOUT)
                try:
                    func(*args, **kwargs)
                except Exception as e:
                    print(e)
                finally:
                    self.tasks.task_done()
            except Empty as e:
                pass
        return

    def signal_exit(self):
        """ Signal to thread to exit """
        self.done.set()


class ThreadPool:
    """Pool of threads consuming tasks from a queue"""
    def __init__(self, num_threads, tasks=[]):
        self.tasks = Queue(num_threads)
        self.workers = []
        self.done = False
        self._init_workers(num_threads)
        for task in tasks:
            self.tasks.put(task)

    def _init_workers(self, num_threads):
        for i in range(num_threads):
            self.workers.append(Worker(self.tasks, i))

    def add_task(self, func, *args, **kwargs):
        """Add a task to the queue"""
        self.tasks.put((func, args, kwargs))

    def _close_all_threads(self):
        """ Signal all threads to exit and lose the references to them """
        for workr in self.workers:
            workr.signal_exit()
        self.workers = []

    def wait_completion(self):
        """Wait for completion of all the tasks in the queue"""
        self.tasks.join()

    def __del__(self):
        self._close_all_threads()


def create_task(func, *args, **kwargs):
    return (func, args, kwargs)

수영장을 이용하려면

from random import randrange
from time import sleep

delays = [randrange(1, 10) for i in range(30)]

def wait_delay(d):
    print('sleeping for (%d)sec' % d)
    sleep(d)

pool = ThreadPool(20)
for i, d in enumerate(delays):
    pool.add_task(wait_delay, d)
pool.wait_completion()

다른 독자를위한 주석 :이 코드는 Python 3 (shebang #!/usr/bin/python3)
Daniel Marschall

왜 값 을 사용 for i, d in enumerate(delays):하고 무시 i합니까?
martineau

@martineau-아마도 그들이 i실행하는 동안 인쇄하고 싶었던 개발의 유물 일 것입니다 .
n1k31t4

create_task거기에 있습니까? 무엇입니까?
MrR

나는 4 투표로 믿을 수 없다. 그래서 파이썬에서 ThreadPooling을하는 방법이다. 공식 파이썬 배포판의 Threadpool이 여전히 손상 되었습니까? 내가 무엇을 놓치고 있습니까?
MrR

2

새 프로세스를 만드는 데 드는 오버 헤드는 최소화되며 특히 4 개에 불과한 경우에는 더욱 그렇습니다. 이것이 응용 프로그램의 성능 핫스팟인지 의심합니다. 간단하게 유지하고 필요한 위치와 프로파일 링 결과가 가리키는 위치를 최적화하십시오.


5
질문자가 Windows (아래에 그가 지정한 것으로 생각하지 않음)에 있다면 프로세스 스핀 업에 상당한 비용이들 수 있다고 생각합니다. 적어도 내가 최근에 한 프로젝트에 관한 것입니다. :-)
Brandon Rhodes

1

내장 스레드 기반 풀이 없습니다. 그러나 Queue클래스 로 생산자 / 소비자 큐를 구현하는 것은 매우 빠릅니다 .

보낸 사람 : https://docs.python.org/2/library/queue.html

from threading import Thread
from Queue import Queue
def worker():
    while True:
        item = q.get()
        do_work(item)
        q.task_done()

q = Queue()
for i in range(num_worker_threads):
     t = Thread(target=worker)
     t.daemon = True
     t.start()

for item in source():
    q.put(item)

q.join()       # block until all tasks are done

3
더 이상 concurrent.futures모듈 의 경우에는 해당되지 않습니다 .
Thanatos

11
나는 이것이 더 이상 사실이라고 생각하지 않습니다. from multiprocessing.pool import ThreadPool
랜달 헌트


0

다른 방법은 프로세스를 스레드 큐 풀에 추가하는 것입니다

import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=cpus) as executor:
    for i in range(0, len(list_of_files) - 1):
        a = executor.submit(loop_files2, i, list_of_files2, mt_list, temp_path, mt_dicto)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.