[개발 정리]
병행 및 병렬 프로그래밍 야매 정리 (3)
hskimim
2024. 8. 18. 22:38
배우는 중이라 틀린 정보가 있을 수 있는데, 업데이트되는 공부 내용이 있으면 계속 고치겠습니다. 교정해주시면 감사하겠습니다..! (_ _)
- concurrency 는 parallelism 과 asyncronous programming 의 pre-condition
- parallelism과 asyncronous programming는 concurrency를 달성하는 수단
- parallelism 에 있는 multi-threading 과 multi-processing 은 os-level 에서 concurrency를 구현
- async
- 개념들
- 코루틴(Coroutine): async로 정의된 비동기 함수로, 이벤트 루프가 관리하는 기본 작업 단위
- 태스크(Task): 코루틴을 래핑한 객체로, 이벤트 루프에 의해 스케줄링되고 실행
- Future: 비동기 작업의 결과를 나타내는 객체로, 태스크와 코루틴의 결과를 나타낼 때 사용됨
- asyncio 에서 헷갈리는 것
- async def func() -> 비동기 함수, 이 함수를 실행하면 실제 함수가 도는게 아니라 코루틴 객체가 나옴
- async def run() : await func() ->
- 또다른 비동기 함수 내에서 실행됨.
- func() 가 도는 동안, run 함수의 실행을 멈춤. 이와 동시에 다른 await 가 돌게 해준다.
- 이벤트 루프에게 제어를 반환. (이벤트 루프는 제어권을 받아서, 다른 await 돌리러 감)
- func() 의 실행이 멈추면 결과값을 반환하고, run의 실행이 마저 진행됨
- await 는 다른 비동기 함수 (async def) 의 실행을(await) 하게 하는 것이기 때문에, 코루틴 객체 단위의 비동기를 지원하는 것임. 그래서 하나의 비동기 함수 안에 await 가 여러개 있으면 순차적으로 돌게 된다.
- 개념들
아래 코드는 하나의 비동기 함수 안에 여러 개의 await 가 있는 것이기에 아래 코드는 순차적으로 돌며 6초정도가 걸린다.
async def func1():
print("Starting func1")
await asyncio.sleep(2) # 첫 번째 2초 대기
await asyncio.sleep(2) # 두 번째 2초 대기
await asyncio.sleep(2) # 세 번째 2초 대기
print("func1 finished")
asyncio.run(func1())
아래 방식이 비동기를 구현한 형태임. 코루틴 객체를 이벤트 루프에 스케줄링 하기 위해 Task 객체로 만들어준다. create_task 를 한 순간 그 즉시 이벤트 루프가 task 를 비동기적으로 실행한다. 이에 따라 task1,2,3 는 거의 동시에 실행된다.
그 아래에 있는 await task1; await task2; await task3 때문에 task 들이 순차적으로 실행되는 것 같아보이지만 create_task 를 하는 순간 실행은 이미 비동기적으로 진행되고, await task1,... 들은 task 가 완료했는지 확인하고 그 결과값을 받아오기 위해 존재하는 것으로 비동기/순차 실행과는 관련이 없다고 한다.
줄여서, await는 태스크의 완료를 기다리고, 그 결과를 처리하거나 다음 작업이 진행될 수 있도록 제어하는 중요한 역할을 한다. asyncio.create_task()는 태스크를 즉시 실행하게 하지만, 그 태스크의 완료를 기다리거나 결과를 얻기 위해서는 await가 필요
import asyncio
async def func1():
print("Starting func1")
await asyncio.sleep(2)
print("func1 finished")
async def func2():
print("Starting func2")
await asyncio.sleep(2)
print("func2 finished")
async def func3():
print("Starting func3")
await asyncio.sleep(2)
print("func3 finished")
async def main():
# 각각의 코루틴을 task로 만들어 실행
task1 = asyncio.create_task(func1())
task2 = asyncio.create_task(func2())
task3 = asyncio.create_task(func3())
# 모든 태스크가 완료될 때까지 기다림
await task1
await task2
await task3
# 메인 비동기 함수를 실행
asyncio.run(main())
다른 방식으로는 asyncio.gather는 여러 비동기 작업을 동시에 실행하고, 모든 작업이 완료될 때 그 결과를 모아서 반환해준다. 이 방식이 더 간단함.
async def func1():
print("Starting func1")
await asyncio.sleep(2)
print("func1 finished")
async def func2():
print("Starting func2")
await asyncio.sleep(2)
print("func2 finished")
async def func3():
print("Starting func3")
await asyncio.sleep(2)
print("func3 finished")
async def main():
await asyncio.gather(
func1(),
func2(),
func3()
)
asyncio.run(main())