멀티 프로세싱을 활용하면 여러 작업을 별도의 프로세스를 생성 후 병렬처리해서 더 빠르게 결과를 얻을 수 있다.
멀티 프로세싱을 잘 활용하면 멀티코어의 CPU 장점을 잘 살릴 수 있지만, 병렬 프로그래밍의 이해 없이 코드를 작성하면 싱글 프로세스보다 더 느린 경우나, 예상하지 못한 결과가 나올 수 있으니 프로세스, 쓰레드에 대한 이해가 필요하다.
* 멀티 프로세싱은 메모리 사용률이 높아지는 단점이 있다.
Process 생성
import os
from multiprocessing import Process
num = 42
def f(name):
global num
num += 1
print('pid of parent:', os.getppid())
print('pid of %s : %d' %(name, os.getpid()))
print('%d' %num)
if __name__ == '__main__':
print('pid of main:', os.getpid())
p1 = Process(target=f, args=("proc_1",))
p2 = Process(target=f, args=("proc_2",))
p1.start(); p1.join()
p2.start(); p2.join()
실행해보면 프로세스 id 가 각각 다른 것을 확인할 수 있다. .start()는 프로세스 시작, .join()은 프로세스 종료, 좀비 프로세스가 생성되는 것을 막기 위해 가급적이면 .join() 을 사용해서 프로세스를 종료시키는 것이 좋다.
pid of parent: 1
pid of __main__ : 237
43
pid of parent: 237
pid of proc_1 : 238
44
pid of parent: 237
pid of proc_2 : 239
44
위 코드를 실행해보면 부모 프로세스가 num 값을 43 으로 만든 것을 자식 프로세스가 각각 상속받아 공유하지 않는 것을 확인할 수 있다. 프로세스간 데이터를 주고 받는 것(혹은 공유 메모리 사용)은 Queue, Pipe, Value, Array 등을 통해 할 수 있다.
Pool
import time
from multiprocessing import Pool
def count(process_name):
for i in range(1, 100001):
print(process_name, i)
if __name__=="__main__":
start_time = time.time()
p_list = ["proc_1", "proc_2", "proc_3", "proc_4"]
pool = Pool(processes = 4)
pool.map(count, p_list)
pool.close()
pool.join()
print(time.time() - start_time)
11번 라인의 processes 값을 바꿔가며 전체 실행시간을 비교해보면 processes 가 높을 수록 실행시간이 줄어드는 경향을 확인할 수 있다.
Pool 과 Process 의 차이
링크 참조
https://www.ellicium.com/python-multiprocessing-pool-process
Queue
from multiprocessing import Process, Queue
def f(q, l:list):
q.put(l)
if __name__ == '__main__':
q = Queue()
p1 = Process(target=f, args=(q,[42, None, 'process 1']))
p2 = Process(target=f, args=(q,[31, None, 'process 2']))
p1.start()
print(q.get()) # prints "[42, None, 'process 1']"
p1.join()
p2.start()
print(q.get()) # prints "[31, None, 'process 2']"
p2.join()
각 프로세스는 Queue를 공유할 수 있다.
Pipe
from multiprocessing import Process, Pipe
def f(conn):
print(conn.recv()) # prints "[31, None, 'send from parent_conn']"
conn.send([42, None, 'send from child_conn'])
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
parent_conn.send([31, None, 'send from parent_conn'])
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "[42, None, 'send from child_conn']"
p.join()
Pipe 를 사용하면 프로세스를 pair 로 생성해서 서로 정보를 주고 받을 수 있다.
라인 10에서 보낸 데이터를 함수 f 를 통해 라인 4에서 받는 것을 볼 수 있다.
Value, Array
from multiprocessing import Process, Value, Array
def f(n, a, num):
n.value = num
for i in range(len(a)):
a[i] = -a[i]
if __name__ == '__main__':
num = Value('d', 0.0)
arr = Array('i', range(10))
p1 = Process(target=f, args=(num, arr, 1))
p1.start()
p1.join()
p2 = Process(target=f, args=(num, arr, 2))
p2.start()
p2.join()
print(num.value)
print(arr[:])
Value, Array 객체를 통해 프로세스간 공유 메모리를 참조하는 것을 확인할 수 있다.
참고:
https://docs.python.org/ko/3/library/multiprocessing.html