multiprocessing 은 threasing 모듈과 유사한 API 를 사용해 process spawning 을 지원하는 패키지이다.
process spawning : 부모 프로세스는 깨끗한 새 파이썬 인터프리터 프로세스를 시작한다. 자식 프로세스는 프로세스 객체의 run() 메서드를 실행하는데 필요한 자원만 상속받는다.
multiprocessing 패키지는 지역과 원격 동시성을 모두 제공하며 thread 대신 sub process 를 사용해 Global Interpreter Lock 을 효과적으로 피한다.
GIL 은 CPython 개발 초기에 번거로운 동시성 관리를 편리하게 하고 thread safe 하지 않은 CPython 의 메모리 관리를 쉽게 하기 위해, GIL 로 파이썬 객체에 대한 접근을 제한하는 형태로 설계되었다.
하나의 thread 가 자원을 독점하는 형태로 실행된다.
하지만 지금처럼 멀티 코어 세상에서는 이 제약이 매우 치명적이다.
multiprocessing 모듈은 프로그래머가 주어진 기계에서 다중 프로세서를 최대한 활용할 수 있도록 한다.
이 객체는 여러 입력 값에 걸쳐 함수의 실행을 병렬 처리하고 입력 데이터를 프로세스에 분산시키는 편리한 방법이다.
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() 메서드가 있다.
두 프로세스가 파이프의 같은 끝에서 동시에 읽거나 쓰려고 하면 파이프의 데이터가 손상될 수 있다.