[docs 따라읽기] multiprocessing

hyeongmo park·2021년 8월 21일
0

소개

multiprocessing 은 threasing 모듈과 유사한 API 를 사용해 process spawning 을 지원하는 패키지이다.

process spawning : 부모 프로세스는 깨끗한 새 파이썬 인터프리터 프로세스를 시작한다. 자식 프로세스는 프로세스 객체의 run() 메서드를 실행하는데 필요한 자원만 상속받는다.

multiprocessing 패키지는 지역과 원격 동시성을 모두 제공하며 thread 대신 sub process 를 사용해 Global Interpreter Lock 을 효과적으로 피한다.

GIL 은 CPython 개발 초기에 번거로운 동시성 관리를 편리하게 하고 thread safe 하지 않은 CPython 의 메모리 관리를 쉽게 하기 위해, GIL 로 파이썬 객체에 대한 접근을 제한하는 형태로 설계되었다.
하나의 thread 가 자원을 독점하는 형태로 실행된다.
하지만 지금처럼 멀티 코어 세상에서는 이 제약이 매우 치명적이다.

multiprocessing 모듈은 프로그래머가 주어진 기계에서 다중 프로세서를 최대한 활용할 수 있도록 한다.

Pool

이 객체는 여러 입력 값에 걸쳐 함수의 실행을 병렬 처리하고 입력 데이터를 프로세스에 분산시키는 편리한 방법이다.

from multiprocessing import Pool

def f(x):
    return x * x
    
if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

----

[1, 4, 9]

with : 자원을 획득하고 사용 후 반납하는 경우 사용, process 5개를 획득한 상황
map(func, data) : 여러 개의 데이터를 한 번에 같은 함수로 처리하는 함수

Process

이 객체는 생성한 후 start() 메서드를 호출해 spawn 한다.

from multiprocessing import Process

def f(name):
    print('hello', name)
    
if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

이 과정에서 참여하는 개별 프로세스 ID 를 보기 위해 예제를 확장한다.

from multiprocessing import Process
import os

def info(title):
    print(title)
    print('module name:', __name__)
    print('parent process:', os.getppid())
    print('process id:', os.getpid())

def f(name):
    info('function f')
    print('hello', name)
    
if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
    
----

main line
module name: __main__
parent process: 21592
process id: 2132
function f
module name: __mp_main__
parent process: 2132
process id: 10652
hello bob

프로세스 간 객체 교환

queue

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])
    
if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())
    p.join()

pipe
Pipe() 함수는 파이프로 연결된 한 쌍의 연결 객체를 돌려주는데 기본적으로 duplex 이다.

from multiprocessing import Process, Pipe

def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()
    
if __name__ == '__main__':
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())
    p.join()

Pipe() 가 반환하는 두 개의 연결 객체는 파이프의 두 끝을 의미한다.
각 연결 객체에는 send() 및 recv() 메서드가 있다.
두 프로세스가 파이프의 같은 끝에서 동시에 읽거나 쓰려고 하면 파이프의 데이터가 손상될 수 있다.

0개의 댓글