java를 처음 배울 때 thread는 병렬실행을 위해 사용한다고 배웠다.
하지만 python을 많이 접하면서 웹프레임워크로 사용을 많이 해왔기 때문에 thread에서 java와 다른 점이 있을거라고는 생각하지 않았다.
1. python의 GIL : global interpreter lock
파이썬을 사용하다보면 편하다고 느낄 때가 있는데, 아무래도 바로바로 결과가 나오는 게 크다. 수정도 쉽고.
python은 그간 사용하던 언어와 다르게 한 줄 씩 실행하는 interpreter언어기 때문이다.
compile언어와 다른 점 때문에 많이 사용하고 좋아했는데 이번 프로젝트에서 이 인터프리터가 조금 문제이다.
python에는 GIL이라는 lock이 있는데, 여러스레드가 동시에 돌아가도록 코드를 짜도 실상으로는 순차적으로 실행이 된다는 말이다. 즉, 내가 처음 배운 병렬실행과는 좀 다르다.
한 스레드가 시작하면 lock이 걸리고, 끝나면 풀리고. 변수 참조를 하나의 스레드만 허용하는 것이다. 하지만 나는 이 gil을 풀어야할 일이 생겼다.
2. gil을 사용하지 않기
gil을 사용하는 걸 해제하면 되지 않을까? 란 생각을 해보았지만 오히려 성능저하가 일어날 수도 있어서 multiprocessing을 사용하는 것이 낫다고 한다.
3. multiprocessing
프로세스가 나뉘는데 어떻게 변수를 공유하지?
이게 가장 큰 문제였다.
지금 사용되는 코드는 쭉 ~ 전역변수를 사용하는 경우가 많았고 역할별로 나누어 병렬로 처리할 건데 검색해도 잘 나오지 않았다.
python muliprocessing 문서에 공유변수에 대한 설명이 있어서 확인해보았다.
미리 메모리에 객체를 할당하고 프로세싱을 시작하면 메인,서브에서 사용할 수 있었다.
공식문서에서는 웬만하면 공유하지 말라고 하는데..
아니그럼 ㅠㅜ 순차실행아니고 병렬필요한데 어떡해요
공유메모리를 사용하는 방법과
from multiprocessing import Process, Value, Array
def f(n, a):
n.value = 3.1415927
for i in range(len(a)):
a[i] = -a[i]
if __name__ == '__main__':
num = Value('d', 0.0)
arr = Array('i', range(10))
p = Process(target=f, args=(num, arr))
p.start()
p.join()
print(num.value)
print(arr[:])
Manager()객체를 통해 사용하는 방법이 있다.
from multiprocessing import Process, Manager
def f(d, l):
d[1] = '1'
d['2'] = 2
d[0.25] = None
l.reverse()
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list(range(10))
p = Process(target=f, args=(d, l))
p.start()
p.join()
print(d)
print(l)