요구사항마다 팬아웃 팬인을 하면 안된다.!!!!
해결: 코루틴을 사용한다.
동시성을 활용하는 함수 사용
async와 await 키워드를 사용하여 구현
제너레이터 실행을 위한 인프라 구축
코루틴 시작 비용은 함수 호출
- 종료할 때까지 1kb미만의 메메모리 사용
- await식에서 일시 중단 후 대기 가능성 상태
#구현 방식
ALIVE = '*'
EMPTY = '-'
class Grid:
...
def count_neighbors(y,x,get):
...
async def game_logic(state, neighbors):
...
#여기서 I/O수행
data = await my_socket.read(50)
...
#step_cell의 정의에서 def 추가 후 game_logic 호출 앞에 await 덧붙이면 코루틴
async def step_cell(y,x, get, set):
state = get(y,x)
neighbors = count_neighbors(y,x,get)
next_state = await game_logic(state, neighbors)
set(y,x, next_state)
#simulate 함수도 코루틴 만들기
import asyncio
async def simulate(grid):
next_grid = Grid(grid.height, grid.width)
tasks = []
for y i range(grid.height):
for x in range(grid.width):
task = step_cell(
y, x, grid.get, next_grid.set) #팬아웃
tasks.append(task)
await asyncio.gather(*tasks) #팬인
return next_grid
step_cell을 호출해도 즉시되지 않고 나중에 coroutine 인스턴스를 반환하여 await식 사용
asyncio 내장 라이브러리가 제공하는 gather 함수는 팬인 수행
모든 실행이 단일 스레드에서 이뤄지고 Grid 인스턴스에 락을 사용할 필요가 없다.
#기존 것에서 한 줄 추가하여 코루틴 실행
class ColumnPrinter:
...
columns = ColumnPrinter()
for i in range(5):
columns.append(str(grid))
grid = asyncio.run(simulate(grid)) #이벤트 루프 실행
print(columns)
#디버깅
async def game_logic(state, neighbors):
...
raise OSError('I.O 문제 발생')
...
asyncio.run(game_logic(ALIVE,3))
#await와 await를 붙이면 요구사항 달성
async def count_neighbors(y,x,get):
...
async def step_cell(y,x,get, set):
state = get(y,x)
neighbors = await count_neighbots(y,x,get)
next_state = await game_logic(state, neighbors)
set(y, x, next_state)
async 키워드로 정의한 함수를 코루틴
코루틴을 호출하는 호출자는 awiat 키워드를 사용해서 자신이 의존하는 코루틴의 결과를 받을 수 있다.
I/O를 병렬화하면서 스레드로 I/O를 수행할 때 발생할 수 있는 문제 극복