파이썬으로 멀티프로세싱 vs 멀티 스레딩


멀티프로세싱이랑 멀티 스레딩 중 어떤 걸 쓰는 게 더 좋은가요? 멀티프로세싱은 GIL을 안 써도 된다고 하던데 이 외에 다른 점이 있나요?

둘의 차이점이랑 장단점을 알려주세요

  • 2016년 01월 27일에 작성됨

조회수 1548


1 답변


좋아요
1
싫어요
채택취소하기

threading은 스레드를 여러개, multiprocessing은 프로세스를 여러개 쓰는걸 의미하고

하나의 프로세스 안에는 여러개 스레드가 있을 수 있기 때문에 프로세스가 스레드보다 더 큽니다.

스레드는 프로세스 내에서 스레드끼리 같은 메모리 공간을 쓰고, 프로세스는 서로 다른 메모리 공간을 씁니다.

따라서 멀티프로세싱을 쓰는 경우 프로세스 간에 같은 객체를 공유하는 게 어렵습니다 항상 객체를 공유하는 게 좋지만은 않은데요, 동시에 여러 개의 스레드가 같은 자원을 쓸 때는 race condition이 발생해 예상치 못한 결과가 날 수 있습니다.

예를 들어 리스트에 원소를 하나씩 append해 mylist = [1,2,3,...,300]을 만들려고 할 때, 스레드를 여러 개 만들어 속도를 높이는 코드를 다음과 같이 만들었다고 하면

import thread, time
mylist = [[0,1]]

def listTo300Elem(id):
    while len(mylist) < 300: #원소가 300개 이상이 되면 멈춤
        mylist.append([id, mylist[-1][1]+1]) #어느스레드가 append했는지 마크

thread.start_new_thread(listTo300Elem, (1,)) #스레드 id 1
thread.start_new_thread(listTo300Elem, (2,)) #스레드 id 2
thread.start_new_thread(listTo300Elem, (3,)) #스레드 id 3
thread.start_new_thread(listTo300Elem, (4,)) #스레드 id 4
thread.start_new_thread(listTo300Elem, (5,)) #스레드 id 5
thread.start_new_thread(listTo300Elem, (6,)) #스레드 id 6
thread.start_new_thread(listTo300Elem, (7,)) #스레드 id 7

time.sleep(5) #충분히 기다려줌
print mylist

결과 :

[[0, 1], [1, 2], [1, 3], [1, 4], [1, 5], ..., [7, 222], [7, 223], [3, 224], [3, 225], [3, 226], [3, 227], [3, 228], [3, 229], [3, 230], [3, 231], [3, 232], [3, 233], [3, 234], [3, 235], [3, 236], [3, 237], [3, 238], [3, 239], [3, 240], [3, 241], [3, 242], [3, 243], [3, 244], [3, 245], [3, 246], [3, 247], [3, 248], [6, 249], [6, 250], [6, 251], [6, 252], [6, 253], [6, 254], [6, 255], [6, 256], [6, 257], [6, 258], [6, 259], [6, 260], [6, 261], [6, 262], [6, 263], [6, 264], [6, 265], [6, 266], [6, 267], [6, 268], [6, 269], [6, 270], [6, 271], [6, 272], [6, 273], [6, 274], [6, 275], [6, 276], [6, 277], [6, 278], [6, 279], [6, 280], [6, 281], [6, 282], [6, 283], [6, 284], [6, 285], [6, 286], [6, 287], [6, 288], [6, 289], [6, 290], [6, 291], [6, 292], [6, 293], [6, 294], [6, 295], [6, 296], [6, 297], [6, 298], [3, 299], [3, 300], [7, 224], [6, 225], [1, 107]]

예상대로라면 마지막 원소는 [어떤 id, 300]이 돼야 하지만 300이 아니라 107이 나왔습니다. 심지어 이 코드는 실행할 때마다 다른 결과가 나옵니다.

이렇게 실행할 때마다 다른 결과를 내는 이유는, thread가 같은 자원을 동시에 접근하기 때문입니다. thread7mylist에 접근해 [7,223]을 출력한 후 block되어 [7, 224]을 출력하기 전, 그 사이에 다른 스레드들이 mylist에 접근해 mylist의 값을 바꿔버립니다. 하지만 thread7은 더 이상 233이 마지막 원소가 아닌걸 모르기 때문에 [7, 224]를 출력한 후 mylist의 길이가 300이상이므로 함수를 종료합니다.

이렇듯 동시에 같은 메모리를 쓰는 상황은 문제가 발생할 수 있기 때문에 이런 상황을 방지하기 위해 자원에 접근을 제한해주는 GIL(global interpreter lock)이 필요합니다.

제가 생각하는 둘의 장단점은

멀티프로세싱

장점

  • 메모리를 공유하지 않음
  • 코드 흐름이 명확함
  • 멀티코어/CPU 의 장점을 쓸 수 있음
  • shared memory를 쓰지 않는 이상 동기화가 필요 없음
  • 자식 프로세스를 interrupt/kill할수 있음
  • 파이썬의 multiprocessing모듈이 제공하는 다양한 interface기능

단점

  • IPC(Inter Process Communication)에서 오버헤드가 좀 더 큼
  • 메모리가 더 많이 필요함

스레딩

장점

  • 메모리가 적게 필요함
  • 메모리를 공유함 - 서로 상태를 공유하기 쉬움
  • GIL을 이용해 병렬 처리가 가능
  • I/O bound 어플리케이션에 옵션이 많음

단점

  • interrupt/kill 할 수 없음
  • command queue/message pump 모델을 따르지 않는 경우 동기화해야 함
  • race condition이 발생할 수 있음
  • 2016년 01월 27일에 작성됨

로그인이 필요한 기능입니다.

Hashcode는 개발자들을 위한 무료 QnA사이트 입니다. 작성한 답변에 다른 개발자들이 댓글을 작성하거나 좋아요/싫어요를 할 수 있기 때문에 계정을 필요로 합니다.
► 로그인
► 계정만들기
Close