Threading / asyncio / multiprocessing / concurrent.futures/ subprocess 특징

About_work·2022년 12월 12일
0

process, thread

목록 보기
8/23
  • 코드 레벨은 다른 문서에서 작성합니다.

Threading

사용 용도

  • 한줄요약: 1개의 주방 / 10개의 요리사 / 10개 해야할 요리
  • blocking I/O 를 해결할 때 사용한다.
  • CPU를 적게 쓰고, I/O waiting 이 많은 테스크에 적합

장점

  • 코드를 가급적 손보지 않고 블로킹 I/O를 병렬로 실행하고 싶을 때는 thread를 사용하는 것이 가장 간편하다.

단점

  • 메모리 lock에 신경을 많이 써야합니다.
  • 스레드를 시작하거나 종료하기를 기다리는 코드에게, 스레드 실행 중에 발생한 예외를 돌려주는 파이썬 내장 기능은 없다. 이로 인해 스레드 디버깅이 어렵다.
  • 에러 메시지 보는 방법

    sys.stderr 출력을 메모리상의 string IO buffer로 전달하여 print 하는 방법
    import io → fake_stderr = io.StringIO()
    import contextlib → with
    contextlib.redirect_stderr(fake_stderr):
    print(fake_stderr.getvalue())

사용시 주의 사항

  • 스레드를 시작하고 실행하는데 비용이 든다.
  • 스레드 하나당 8MB의 메모리가 더 필요하다.
  • 동시성 작업마다 스레드를 하나씩 실행하면, 스레드 개수가 많아지면 최악의 결과를 낳는다.
  • 스레드 삭제하고 다시 만드는게, 꼭 필요한지를 따져보고 진행하자.

asyncio

사용 용도

  • 한줄요약: 1개의 주방 / 1개의 chef / 10개의 해야할 요리
  • I/O 대기 시간의 효율적 관리
  • 시스템 콜(블록킹 I/O와 스레드 시작도 포함해)을 코루틴으로 만들면, 프로그램의 응답성이 좋아지고 사용자가 느끼는 지연 시간을 줄일 수 있다.
  • asyncio는 multiple servers와 연결된 servers와 clients 의 선택 도구로 사용되야 한다.
  • 각 테스크들의 switch 시점을 알고 있다면, multi threading을 이용할 필요가 없다.
  • request 요청 시점과 - 결과 반환 시점을 알 수 있다면 → 보다 효율적으로 다른 작업으로 전환할 수 있다.

개념

coroutine

  • async 키워드로 정의한 함수
  • 코루틴을 호출하는 호출자는, await 키워드를 사용해 자신이 의존하는 코루틴의 결과를 받을 수 있다.

특징

  • single process / single thread
  • asyncio는 coroutines를 이용하여 쓰인다. (coroutines는 await를 이용하여 흥미로운 일이 일어날 때까지 parallel하게 기다린다(suspend))
  • suspending은 blocking이랑 다르다. suspend는 event loop thread를 허용한다. → 다른 것들을 계속하기 위해
  • 활성화된 코루틴은 종료될 때까지 1KB 미만의 메모리를 사용한다.
  • generator을 실행하기 위한 infra를 사용한다.
  • generator
    - iterator을 생성해주는 함수. 함수안에 yield 키워드를 사용합니다.
  • 스레드와 마찬가지로, 코루틴도 환경으로부터 입력을 소비하고 결과를 출력할 수 있는 독립적인 함수다.
  • 코루틴은 매 await 식에서 일시 중단되고, 일시 중단된 대기 가능성이 해결된 다음에, async 함수로부터 실행을 재개한다는 차이점이 있다.
  • 외부 환경에 대한 명령(예: I/O)와, 원하는 명령을 수행하는 방법을 구현하는 것(예: 이벤트 루프)를 코드에서 분리해준다.
  • asyncio 내장 모듈을 사용하면, thread와 블로킹 I/O를 사용하는 기존 코드를 → 코루틴과 비동기 I/O를 사용하는 코드로 쉽게 포팅할 수 있다.

장점

  • 작업의 진행 상황을 반복적으로 측정하는 event loop 가 존재하여, 각 작업들을 효율적으로 관리해주므로 → I/O 대기 시간이 효율적으로 관리됩니다.
  • 많은 양의 동시성 함수 제공
  • 다른 멀티 스레딩 패키지들은 "non-blocking APIs" 라는 점에서 비동기이지만, 여전히 thread/process pools가 알아서 하는 것에 의존하고 있다.
  • asyncio는 "board에 걸쳐서 async system calls를 이용" 한다.
  • blocking call이 전혀 없다. asyncio.run() entry point가 유일한 blocking part이다.
  • thread-based solution과 비교했을 때, 시스템을 멈추지 않고도 가벼운 task를 수천 개 spawn 할 수 있다.
  • tasks를 한번에 취소하거나 기다리기 쉽다.
  • 코루틴이 여러개 수행되더라도, 단일 스레드에서 수행하기 때문에 lock을 사용할 필요가 없다. I/O 는 asyncio가 제공하는 이벤트 루프의 일부분으로 병렬화된다.
  • 이벤트 루프로 코루틴을 가능하게 한다.
    - 다수의 I/O을 효율적으로 동시에 실행할 수 있고, 이벤트 루프에 맞춰 작성된 함수들을 빠르게 전환해가며 골고루 실행할 수 있다.
  • 코루틴은 스레드와 달리 아래의 것들이 필요 없다.
  • 메모리 부가 비용
  • 스레드 시작 비용
  • 컨텍스트 전환 비용
  • 복잡한 lock(동기화 코드) 이용

단점

  • 파이썬은 for 루프, with 문, generator, comprehension 의 비동기 버전(asyncio에서)을 제공.
  • 하지만, next와 iter 내장 함수에 대응하는 비동기 함수는 없다.
  • yield from에 대응하는 비동기 버전도 없다.
  • 하지만 빠르게 추가될 것으로 보인다.

multiprocessing

사용 용도

  • 한줄 요약: 10개의 주방 / 10개의 요리사 / 10개 해야할 요리
  • parallel 을 확보하기 위함
  • CPU를 많이 쓰고, I/O waiting 이 적은 테스크에 적합
  • processpool executor와 비교했을 때, multi processing의 장점
  • 공유 메모리를 사용하는 더 고급스러운 기능을 제공한다.
  • 하지만, 이러한 고급 기능을 사용하는 것은 매우 복잡하며, 한 프로세스 내에서 여러 스레드 사이에 공유되는 메모리 공간을 추론하기가 쉽지 않다.
  • 그러한 복잡도를 다른 프로세스까지 확장하고 소켓까지 추가하면 프로그램의 동작을 이해하기 더 어려워진다.
  • 이 고급스러운 기능 사용은 다른 모든 패키지들을 사용해보고도 안되면, 제일 마지막 단계에 시도하라.

장점

단점


concurrent.futures

사용 용도

  • ThreadpoolExecutor: blocking I/O 병렬성 확보
  • ProcesspoolExecutor: 병렬성 확보

ThreadpoolExecutor, ProcesspoolExecutor 공통

장점

  • 특정 작업을 thread <-> process 로 전환이 필요할 때 쉽게 변경할 수 있다.
    • 한정된 리팩터링만으로 간단한 I/O 동시성 <-> 병렬성 전환을 활성화할 수 있다. (threadpoolexecutor <-> processpoolexecutor)
  • futures 라는 것을 쓸 수 있음
    • 아직 완료되지 않은 (혹은 완료되었는지 당장은 모르는) 작업을 외부에서 객체로 다룰 수 있게 된다.
    • 예외를 디버깅하기 쉬움: result 메서드를 호출하면 스레드를 실행하는 중에 발생한 예외를 자동으로 전파시켜줌
  • submit 1번만 해줘도, 알아서 max_workers 내에서 조절하여 여러 thread로 작업을 처리해 주는 것 같다. (확인 필요)
  • pool의 장점을 취할 수 있다.(multiprocessing 패키지에서도 제공)
    • 프로세스 혹은 스레드에 하나의 task만 배당하여 실시하는 것이 아니라, task가 끝나도 프로세스 혹은 스레드를 제거하지 않고, 다른 task를 이어서 수행할 수 있다.
    • 즉, 삭제하고 재시작하는데 드는 비용을 줄일 수 있다.

단점

  • max_workers의 개수를 미리 지정해야 하므로 I/O 병렬성 혹은 병렬성을 제한한다.
  • graceful shutdown을 위해 정밀한 설계 필요 (ctrl+c 한번으로 종료시키기 어렵다.)
    • future이 완료되어야만 shutdown이 된다.
    • 현재 실행 중인 future 은 취소할 수 없다.

ThreadpoolExecutor

장점

  • Thread와 Queue의 장점들을 모두 취할 수 있음.

ProcesspoolExecutor

특징

  • 동작 방식은 아래와 같다 (예시로 설멍)

    • (부모) 얻은 원소를 pickle 모듈을 사용해 이진 데이터로 직렬화한다.
    • (부모, 자식) 이 객체는 로컬 소켓을 통해, 주 인터프리터 프로세스 → 자식 인터프리터 프로세스에게 1번에서 직렬화한 데이터를 복사한다.
    • (자식) 이 객체는 pickle을 이용해 데이터를 파이썬 객체로 역직렬화한다.
    • (자식) 함수 실행 후, 결과를 이진 데이터로 직렬화한다.
    • (부모, 자식) 로컬 소켓을 통해 전달
    • (부모) 데이터를 파이썬 객체로 역직렬화

subprocess

사용 용도

  • 자식 프로세스를 실행하고, 입력과 출력 stream을 관리할 수 있다.

장점

단점

profile
새로운 것이 들어오면 이미 있는 것과 충돌을 시도하라.

0개의 댓글