728x90

동기 vs 비동기

일반적인 동기식 프로그래밍에서는 작업이 순차적으로 실행되며, 한 작업이 완료될 때까지 다음 작업을 기다림

반면에 비동기식 프로그래밍에서는 작업을 병렬로 실행하고, 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속할 수 있음

비동기 프로그래밍은 주로 입출력이나 네트워크 작업과 같이 시간이 오래 걸리는 작업을 수행할 때 유용함

이를 통해 여러 작업을 동시에 처리할 수 있고, 시스템 자원을 효율적으로 활용할 수 있음

asyncio

asyncio는 파이썬의 비동기 프로그래밍을 지원하는 라이브러리로, 이벤트 기반의 비동기 프로그램을 작성할 때 유용함

대규모의 동시성 작업을 처리하고, 네트워크와 파일 입출력을 효율적으로 다룰 수 있게 해 줌

asyncio의 핵심은 코루틴이벤트루프라는 개념

이벤트 루프는 비동기 작업을 관리하고 실행하는 역할이고, 코루틴은 비동기 함수로 중단된 지점에서 실행을 일시 중단하고 다른 작업을 수행한 뒤 재개할 수 있게 함

asyncio에서의 코루틴

코루틴은 async def 키워드를 사용하여 정의됨

asyncio에서는 await 키워드를 사용하여 코루틴을 호출하고 실행 

import asyncio

async def example_coroutine():
    print("Coroutine started")
    await asyncio.sleep(1)
    print("Coroutine finished")

async def main():
    await example_coroutine()

asyncio.run(main())

asyncio에서의 이벤트 루프

asyncio에서 이벤트 루프는 비동기 작업을 관리하고 실행하는 핵심 요소

이벤트 루프는 이벤트를 감지하고 처리하는 무한 루프로, 비동기 작업을 실행하고 완료될 때까지 대기하는 역할을 함

이를 통해 여러 비동기 작업을 동시에 실행하고, 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있음

시간이 오래 걸리는 입출력 작업을 비동기적으로 처리하여 프로그램의 성능을 향상하는 데 유용함

 

asyncio에서는 asyncio.get_event_loop 함수를 사용하여 이벤트 루프를 얻고, asyncio.run 메서드를 사용하여 비동기 작업을 실행함

import asyncio
import aiofiles

async def write_to_file(file_name, content):
    print(f"Writing to file: {file_name}")
    async with aiofiles.open(file_name, 'w') as file:
        await file.write(content)
        print(f"Content successfully written to {file_name}")

async def main():
    file_contents = [
        ("file1.txt", "Hello, World!"),
        ("file2.txt", "Async I/O is awesome!"),
        ("file3.txt", "Python is powerful!")
    ]

    # 비동기 파일 쓰기 작업 등록
    tasks = [write_to_file(file_name, content) for file_name, content in file_contents]

    # 비동기 작업 실행
    await asyncio.gather(*tasks)

# 이벤트 루프를 생성하여 main 함수 실행
asyncio.run(main())
더보기

실행결과

Writing to file: file1.txt
Writing to file: file2.txt
Writing to file: file3.txt
Content successfully written to file1.txt
Content successfully written to file2.txt
Content successfully written to file3.txt

asyncio에서의 동시성 제어

동시성 제어는 여러 작업이 동시에 실행될 때 발생하는 문제를 관리하고 해결하는 것

경쟁 조건과 교착 상태 같은 문제를 방지하여 프로그램의 안정성을 보장함

asyncio에서는 Lock, Semaphore, Queue 등의 동시성 제어 도구를 이용하여 여러 작업 간의 상호작용을 조절하고, 동기화를 유지할 수 있음

 

Lock를 이용한 동시성 제어 예제 코드

import asyncio

async def worker(lock, task_name):
    print(f"{task_name} is trying to acquire the lock")
    async with lock:
        print(f"{task_name} has acquired the lock")
        await asyncio.sleep(1)
        print(f"{task_name} is releasing the lock")

async def main():
    # 생성된 Lock 객체
    lock = asyncio.Lock()
    # 두 개의 작업이 동시에 실행되지만, 하나의 작업만이 lock을 확보하도록 하여 동시성 제어
    await asyncio.gather(
        worker(lock, "Task 1"),
        worker(lock, "Task 2")
    )

asyncio.run(main())
더보기

실행결과

Task 1 is trying to acquire the lock
Task 1 has acquired the lock        
Task 2 is trying to acquire the lock
Task 1 is releasing the lock
Task 2 has acquired the lock
Task 2 is releasing the lock

 

Semaphore를 이용한 동시성 제어 예제 코드

import asyncio

async def worker(semaphore, task_name):
    async with semaphore:
        print(f"{task_name} is entering the semaphore")
        await asyncio.sleep(1)
        print(f"{task_name} is leaving the semaphore")

async def main():
    # Semaphore 객체 생성
    semaphore = asyncio.Semaphore(2)  # 최대 2개의 작업을 동시에 실행
    # Semaphore를 공유하는 세 개의 작업
    await asyncio.gather(
        worker(semaphore, "Task 1"),
        worker(semaphore, "Task 2"),
        worker(semaphore, "Task 3")
    )

asyncio.run(main())
더보기

실행결과

Task 1 is entering the semaphore
Task 2 is entering the semaphore
Task 1 is leaving the semaphore
Task 2 is leaving the semaphore
Task 3 is entering the semaphore
Task 3 is leaving the semaphore

 

Queue를 이용한 동시성 제어 예제 코드

import asyncio

async def producer(queue):
    for i in range(5):
        print(f"Producing {i}")
        await queue.put(i)
        await asyncio.sleep(1)

async def consumer(queue, consumer_name):
    while True:
        item = await queue.get()
        print(f"{consumer_name} consumed {item}")
        queue.task_done()

async def main():
    # 크기가 3인 Queue 생성
    queue = asyncio.Queue(maxsize=3) # 최대 3개의 작업을 동시에 실행

    # Producer와 Consumer 생성
    producer_task = asyncio.create_task(producer(queue))
    consumer_task1 = asyncio.create_task(consumer(queue, "Consumer 1"))
    consumer_task2 = asyncio.create_task(consumer(queue, "Consumer 2"))

    # Producer가 종료될 때까지 기다림
    await producer_task
    # Consumer가 모든 작업을 처리할 때까지 기다림
    await queue.join()

    # Consumer들에게 종료를 알림
    consumer_task1.cancel()
    consumer_task2.cancel()

asyncio.run(main())
더보기

실행결과

Producing 0
Consumer 1 consumed 0
Producing 1
Consumer 1 consumed 1
Producing 2
Consumer 2 consumed 2
Producing 3
Consumer 1 consumed 3
Producing 4
Consumer 2 consumed 4

728x90

'Python' 카테고리의 다른 글

Python 문서화 (feat. PEP 257)  (0) 2024.04.11
Python ZeroMQ  (0) 2024.04.11
Python websocket  (0) 2024.04.08
Python 루틴, 서브루틴, 코루틴  (1) 2024.04.04
Python multiprocessing  (0) 2024.04.04

+ Recent posts