[Advanced Python] -4
Coroutine
코루틴
: 루틴의 일종으로서, 협동 루틴이라 할 수 있다.
루틴과 서브 루틴은 서로 비대칭적인 관계이지만, 코루틴들은 완전히 대칭적인, 즉 서로가 서로를 호출하는 관계이다.
-> 어떠한 코루틴이 발동될 때마다 해당 코루틴은 이전에 자신의 실행이 마지막으로 중단되었던 지점 다음의 장소에서 실행을 재개한다.
- 일반 함수를 호출하면 코드를 한 번만 실행할 수 있지만, 코루틴은 코드를 여러 번 실행할 수 있습니다.
- 함수의 코드를 실행하는 지점을
진입점(entry point)
이라고 하는데, 코루틴은 진입점이 여러 개인 함수입니다. - 코루틴 바깥에서 값을 받아오면서 값을 다시 바깥으로 전달하는 방법
- 변수 = (yield 변수)
- 변수 = next(코루틴객체) -> 코드는 실행하지만 값은 보내지 않는다.
- 변수 = 코루틴객체.send() -> 코드를 실행하고 값을 보낸다.
generator
: next 함수를 반복 호출하여 값을 얻어낸다.
coroutine
: next 함수를 한번만 호출한 뒤 send로 값을 주고 받는 방식이다.
def number_coroutine():
while True: # 코루틴을 계속 유지하기 위해 무한 루프 사용
x = (yield) # 코루틴 바깥에서 값을 받아옴, yield를 괄호로 묶어야 함
print(x)
co = number_coroutine()
next(co) # 코루틴 안의 yield까지 코드 실행(최초 실행)
# co.send(None), co.__next__()
co.send(1) # 코루틴에 숫자 1을 보냄
co.send(2) # 코루틴에 숫자 2을 보냄
co.send(3) # 코루틴에 숫자 3을 보냄
# 1
# 2
# 3
- 코루틴을 종료하는 방법
- 코루틴객체.close()
def number_coroutine():
while True:
x = (yield)
print(x, end=' ')
co = number_coroutine()
next(co)
for i in range(20):
co.send(i)
co.close() # 코루틴 종료
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
- 코루틴 객체에서 close메소드를 호출하면 코루틴이 종료될 때
GeneratorExit
예외가 발생한다. -> 이 예외를 처리하면 코루틴의 종료 시점을 알 수 있다.
def number_coroutine():
try:
while True:
x = (yield)
print(x, end=' ')
except GeneratorExit: # 코루틴이 종료 될 때 GeneratorExit 예외 발생
print()
print('코루틴 종료')
co = number_coroutine()
next(co)
for i in range(20):
co.send(i)
co.close()
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# 코루틴 종료
- 코루틴 안에 예외를 발생시키는
throw
메소드를 사용하여 코루틴을 종료시킬 수 있다.- 코루틴객체.throw(예외이름, 에러메시지)
def sum_coroutine():
try:
total = 0
while True:
x = (yield)
total += x
except RuntimeError as e:
print(e)
yield total # 코루틴 바깥으로 값 전달
co = sum_coroutine()
next(co)
for i in range(20):
co.send(i)
print(co.throw(RuntimeError, '예외로 코루틴 끝내기'))
# 예외가 발생하면 에러 메시지를 출력하고
# 코루틴의 except에서 yield로 전달받은 누적된 값을 출력
# 예외로 코루틴 끝내기
# 190
- 첫번째 예제 코드는 greeting에 문자열이 send 보낼때 마다 계속 더해지는 문제가 있습니다. 다음과 같이 send 호출시마다 good morning, good afternoon, good evening 이 출력되도록 코드를 수정해보세요
import time
def coroutine_test():
greeting = "good"
while True:
text = (yield greeting)
print("text= ", end= ""), print(text)
greeting += text
if __name__ == "__main__":
cr = coroutine_test()
print("cr=", end=""), print(cr)
next(cr) # 코루틴 최초 실행 'good' 저장
time.sleep(2)
print("send 1")
print(cr.send("morning")) # 마지막으로 저장된 'good' 뒤에서 실행을 재개
time.sleep(2)
print("send 2")
print(cr.send("afternoon"))
time.sleep(2)
print("send 3")
print(cr.send("evening"))
time.sleep(2)
# cr=<generator object coroutine_test at 0x000001F301A0CF90>
# send 1
# text= morning
# goodmorning
# send 2
# text= afternoon
# goodmorningafternoon
# send 3
# text= evening
# goodmorningafternoonevening
# 해결방법1
def coroutine_test():
greeting = "good"
while True:
text = (yield greeting)
print(greeting + text)
if __name__ == "__main__":
cr = coroutine_test()
print("cr=", end=""), print(cr)
next(cr)
time.sleep(2)
print("send 1")
cr.send("morning")
time.sleep(2)
print("send 2")
cr.send("afternoon")
time.sleep(2)
print("send 3")
cr.send("evening")
time.sleep(2)
# cr=<generator object coroutine_test at 0x0000026EAF53CF90>
# send 1
# goodmorning
# send 2
# goodafternoon
# send 3
# goodevening
# 해결방법2
def coroutine_test():
greeting = "good"
while True:
text = (yield greeting)
print("text = ", end=""), print(text)
greeting = "good" # greeting 초기화
greeting += text
if __name__ == "__main__":
cr = coroutine_test()
print("cr=", end=""), print(cr)
next(cr)
time.sleep(2)
print("send 1")
print(cr.send("morning"))
time.sleep(2)
print("send 2")
print(cr.send("afternoon"))
time.sleep(2)
print("send 3")
print(cr.send("evening"))
time.sleep(2)
# cr=<generator object coroutine_test at 0x000001743551CF90>
# send 1
# text = morning
# goodmorning
# send 2
# text = afternoon
# goodafternoon
# send 3
# text = evening
# goodevening
- 두번째 코드를 coroutine 과 asyncio 를 활용하여 구현해 보세요. asyncio에는 비동기 처리를 위한 event loop가 존재하고 여기에 코루틴을 등록해서 사용하는 구조로 되어있습니다
# 코루틴 2개로 일억을 증가시키는 예제
def coroutine_1():
return_value = 0
while True:
input_value = (yield return_value)
return_value = input_value + 1
def coroutine_2():
return_value = 0
while True:
input_value = (yield return_value)
return_value = input_value + 1
if __name__ == "__main__":
ret_value = 0
c1 = coroutine_1()
c2 = coroutine_2()
next(c1)
next(c2)
while ret_value < 100000000:
ret_value = c1.send(ret_value)
ret_value = c2.send(ret_value)
print("ret_value =", end=""), print(ret_value)
print("end of main")
# ret_value =100000000
# end of main
asyncio(Asynchronous I/O)
- 비동기 프로그래밍을 위한 모듈이며 CPU 작업과 I/O를 병렬로 처리하게 해줍니다.
- 고수준으로 구조화된 네트워크 구조에 적합하다.
- 코루틴들을 동시에 실행하고 실행을 제어한다.
- 동기 처리 : 특정 작업이 끝나면 다음 작업을 처리하는 순차처리 방식
- 비동기 처리 : 여러 작업을 처리하도록 예약한 뒤 작업이 끝나면 결과를 받는 방식
import asyncio
async def coroutine_1(num): # 네이티브 코루틴 생성
result_value = 0
while result_value < num:
result_value += 1
return result_value
async def coroutine_2(num):
result_value = 0
while result_value < num:
result_value += 1
return result_value
async def main():
# await : 해당 객체가 끝날 때까지 기다린 뒤 결과 반환
one = await coroutine_1(50000000)
two = await coroutine_2(50000000)
# await asyncio.sleep(1.0) -> 1초 대기, asyncio.sleep도 네이티브 코루틴
print("ret_value=", end="")
print(one+two)
print("end of main")
if __name__ == "__main__":
loop = asyncio.get_event_loop() # 이벤트 루프를 얻음
loop.run_until_complete(main()) # main이 끝날 때까지 기다림
loop.close() # 이벤트 루프를 닫음
# ret_value=100000000
# end of main
Author And Source
이 문제에 관하여([Advanced Python] -4), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@kimkrh/Advanced-Python-4저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)