Python
Generator, Decorator, GIL 등
멀티프로세싱은 어떻게 동작하는가
메모리 영역에서 어떤일이 발생하는가
멀티프로세싱, 멀티스레딩, 동시성, 병렬성, 뭐 이렇게 어려운게 많을까?
이번에는 사실 멀티프로세싱은 어떻게 동작하는가 에 대해 정리할 차례지만
FastAPI프로젝트에서 사용했던 async, await, run_in_threadpool 등에 대해 의문이 생겨 공부하다가 파이썬의 asyncio까지 오게 돼서 asyncio에 대해 정리하려한다.
일단 동시성과 병렬성에 차이부터 확인해보자.
여기 블로그의 첫 그림을 보면 바로 이해된다.
동시성(Concurrency)의 경우 싱글코어에서 여러 thread를 왓다갓다 하면서 처리한다. 옛날에 단일코어 컴퓨터의 경우 멀티테스킹은 사실 진짜 멀티테스킹이 아닌 이치다. cpu는 특정시간에 단 하나의 명령을 수행하게 되어있다. 왓다갓다 하는 부분은 문맥교환(context switching)인데 이게 빨라서 동시에 처리되는것처럼 보인다. context switching은 다음 정리글이다.
병렬성(Parallelism)의 경우 멀티코어에서 thread 여러개가 정말 병렬적으로 실행된다. 각 코어마다 각 thread를 담당해서!
갑자기 왜 동시성과 병렬성의 차이냐고?!
asyncio는 Concurrency를 위해 있는 라이브러리이기 때문이다. 맞다. 사실 내가 헷갈려서 적었다.
아무튼 python docs에서 예시도 줬으니 예시도 한 번 해보자.
영상이 아니라 아쉽다.
Hello....하고 1초 후에 ....World!가 출력되었다.
근데?.. time.sleep(1)하고 해도 똑같잖아 라고 생각할 수 있다. 그럼 왜 이딴 예시를 넣어줬냐? 나도 궁금하다. 찾아보자.
오늘의 참고글은 여기다
위키독스
다음과 같은 코드가 있다. 실행해보자.
우리는 result1을 만들기 위해 2초를 잔다. 근데 난 잠든 2초동안은 result2를 만들고 싶다.
위의 코드를 확인해보기 전에 코루틴이라는 개념을 알고갈 필요가 있다 ! 제너레이터의 yield도 코루틴이라고 불리는데 구분하기위해 여기는 네이티브 코루틴이라고 나눈다고 한다.위와 같은 흐름을 따른다 ! 이전에 정리했던 generator도 코루틴이다!
대충 이렇게까지만 알고 위의 코드를 실행해보자.
작업중= A,B를 보면 순서가 정상적이지 않다. A를하다가 B를한다 !!
작업 A에 걸린시간은 2초고 작업 B에 걸린시간은 3초인데 총 시간이 3초다.
이제 컴퓨터한테 자면서도 일하라고 시킬수 있다!!
코드를 살펴보자.
함수를 비동기적으로 실행시키고 싶다면 함수앞에 async만 붙여주면 된다. 이제 이 함수는 비동기 함수이고 코루틴이라 부른다!
코루틴 안에서 다른 코루틴을 부르고 싶다면 await 을 붙여주고 호출하자.
fastapi docs인가, python docs인가에서는 어느블로그였나는 말그대로 await은 함수가 끝날때까지 기다리겠다는 뜻이라고 설명되어있다. 반은 맞고 반은 틀린말이라고 생각한다. await은 그냥 기다리지 않는다.
await 코루틴
을 만난다면 await로 호출한 코루틴이 종료될 때까지 기다리지 않고 제어권을 메인 쓰레드나 다른 코루틴으로 넘긴다. (이러한 방식을 넌블록킹(non-blocking) 방식이라고 한다) 그리고 호출한 코루틴이 종료되면 이벤트에 의해 다시 그 이후 작업이 수행된다!
asyncio.create_task는 수행할 코루틴의 작업(Task)을 생성한다. 이 때 작업을 생성할 뿐이지 실제로 코루틴이 수행되는 것은 아니다. 실제 코루틴 실행은 await 태스크에 의해 실행된다. 그리고 실행 태스크의 결과 값은 태스크.result()로 얻을 수 있다.
asyncio.create_task는 코루틴을 동시에 실행하기 위해 반드시 필요하다. create_task가 아닌 await로 코루틴을 실행할 경우에는 코루틴이 동시에 실행되지 않고 하나씩 차례로 실행되어 이득이 없을 것이다.
result1 = await sum("A", [1, 2])
result2 = await sum("B", [1, 2, 3])
task1 = asyncio.create_task(sum("A", [1, 2]))
task2 = asyncio.create_task(sum("B", [1, 2, 3]))
await task1
await task2
result = task1.result()
result = task2.result()
코드상으로는 훨씬 지저분하고 어렵다 !
하지만 분명 알아두면 유용하게 쓸 수 있을 것이다.
async def main(): pass if __name__ == "__main__": asyncio.run(main())
코루틴인 메인함수는 asyncio.run으로 실행하자 !
https://www.charlezz.com/?p=44646
https://wikidocs.net/125092
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=indpkr&logNo=220064291506