TechY

병행 및 병렬 프로그래밍 야매 정리 (3) 본문

[개발 정리]

병행 및 병렬 프로그래밍 야매 정리 (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())