SEOYUL
산호심는 블로그
SEOYUL
  • 분류 전체보기 (100)
    • IT (70)
      • Go (3)
      • Linux (34)
      • Python +Django (31)
      • RaspberryPi (2)
    • Memo (27)
      • 멀티캠퍼스 (26)

인기 글

최근 댓글

최근 글

hELLO · Designed By 정상우ⓒMIT.
SEOYUL

산호심는 블로그

파이썬 코루틴 Coroutine
IT/Python +Django

파이썬 코루틴 Coroutine

2020. 10. 5. 16:24

Coroutine 의 개념

  • yield 키워드 : 메인 루틴과 서브 루틴의 연결 기능을 해준다. 멈춰있다가 다음 루틴을 제어
  • 코루틴의 제어, 코루틴 상태, 양방향 값 전송을 알아본다.
  • yield from 코루틴

서브 루틴 : 메인 루틴에서 리턴에 의해 호출 부분으로 돌아와 다시 프로세스

쓰레드 : 지금까지 해온것은 싱글 쓰레드 작업, 멀티 쓰레드는 공유 자원의 교착 상태를 관리해야 하기 때문에 복잡하다. 컨텍스트 스위칭 비용 발생과 자원 소비 비용 고려

코루틴

루틴 실행 중 멈춤 가능, 특정 위치로 돌아갔다가 다시 원래 위치로 돌아와 수행

비동기 처리 방식으로 동시성 프로그래밍을 구현할 수 있다. 코루틴은 제너레이터를 기반으로 작동함

코루틴은 스케쥴링 오버헤드가 매우 적다. (= 컨텍스트 스위칭 비용이 적다)

항상 처음은 코루틴 next() 한번을 보내 다음 yield 까지 실행한다. 바로 send 할 수 없다.

send(x) x값이 yield 에 들어간다.

예를들어 n = yield k

이면 send(10) 을 보내면 이전에 k 는 none yield 에 10 따라서 n = 10 다음 send에 보임

def coroutine1():
    print('>>> coroutine started.')
    i = yield
    print('>>> coroutine received : {}'.format(i))

# 제너레이터 선언
c1 = coroutine()
print(c1, type(c1))
# <generator object coroutine1 at 0x000001FB0BCDDCC8> <class 'generator'>

# yield 실행 전까지 진행
next(c1)
# >>> coroutine started.

# 기본으로 None 전달
next(c1)
# >>> coroutine received : None

# 값 전송
# c1.send(100)

# 잘못된 사용
c2 = coroutine1()
# c2.send(100) # 예외 발생, 코루틴은 next() 로 시작해야 함

코루틴 내장 함수

GEN_CREATED : 처음 대기 상태

GEN_RUNNING : 실행 상태

GEN_SUSPENDED : yield 대기 상태

GEN_CLOSED : 실행 완료 상태

def coroutine2(x):
    print('>>> coroutine started : {}'.format(x))
    y = yield x
    print('>>> coroutine received : {}'.format(y))
    z = yield x + y
    print('>>> coroutine received : {}'.format(z))

c3 = coroutine2(10)

from inspect import getgeneratorstate

print(getgeneratorstate(c3))
# GEN_CREATED

print(next(c3))
# >>> coroutine started : 10
# 10

print(getgeneratorstate(c3))
# GEN_SUSPENDED

print(c3.send(15))
# >>> coroutine received : 15
# 25

print(c3.send(20)) # 다음 yield가 없어서 예외 발생 StopIteration
# >>> coroutine received2 : 20
"""
Traceback (most recent call last):
# print(c3.send(20))
StopIteration
"""

데코레이터 패턴을 사용하는 코루틴

from functools import wraps

def coroutine(func):
    '''Decorator run until yield'''
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)                   # 데코레이터를 사용해서 첫 next 를 항상 해주게 함
        return gen
    return primer

@coroutine
def sumer():
    total = 0
    term = 0
    while True:
        term = yield total
        total += term

su = sumer()

print(su.send(100))
print(su.send(40))
print(su.send(60))

예외 처리를 통한 코루틴

class SampleException(Exception):
    '''설명에 사용할 예외 유형'''

def coroutine_except():
    print('>> coroutine stated.')
    try:
        while True:
            try:
                x = yield
            except SampleException:
                print('-> SampleException handled. Continuing..')
            else:
                print('-> coroutine received : {}'.format(x))
    finally:
        print('-> coroutine ending')

exe_co = coroutine_except()

print(next(exe_co))
print(exe_co.send(10))
print(exe_co.send(100))
print(exe_co.throw(SampleException)) # 강제로 예외 발생시켜도 코루틴이 안닫힘
print(exe_co.send(1000))
print(exe_co.close()) # GEN_CLOSED

리턴값이 있는 코루틴

# 평균 값을 계속 리턴해주는 코루틴
def averager_re():
    total = 0.0
    cnt = 0
    avg = None
    while True:
        term = yield
        if term is None:
            break
        total += term
        cnt += 1
        avg = total / cnt
    return 'Average : {}'.format(avg)

avger2 =averager_re()

next(avger2)

avger2.send(10)
avger2.send(30)
avger2.send(50)

try:
    avger2.send(None)
except StopIteration as e:
    print(e.value)
# 예외처리의 value 값 문자열 리턴을 확인 가능

중첩 코루틴 처리

yeild from 이 자동으로 StopIteration 을 처리해준다. 3.7 이후로 await 으로 명칭 변경

def gen1():
    for x in 'AB':
        yield x
    for y in range(1,4):
        yield y

t1 = gen1()

print(next(t1))
# A
print(next(t1))
# B
print(next(t1))
# 1
print(next(t1))
# 2
print(next(t1))
# 3
# print(next(t1))
# 에러

t2 = gen1()

print(list(t2))
# ['A', 'B', 1, 2, 3]

def gen2():
    # gen1 보다 빠르게 순차적 처리를 잘해줌
    yield from 'AB'
    yield from range(1,4)

t3 = gen2()

print(next(t3))
# A
print(next(t3))
# B
print(next(t3))
# 1
print(next(t3))
# 2
print(next(t3))
# 3
# print(next(t3))
# 에러

t4 = gen2()

print(list(t4))
# ['A', 'B', 1, 2, 3]

def gen3_sub():
    print('Sub coroutine.')
    x = yield 10
    print('Recv : ', str(x))
    x = yield 100
    print('Recv : ', str(x))

def gen4_main():
    yield from gen3_sub()

t5 = gen4_main()

print(next(t5))
# Sub coroutine.
# 10

print(t5.send(7))
# Recv : 7
# 100

print(t5.send(77))
# Recv : 77
# 예외 발생
# StopIteration
저작자표시 비영리 변경금지 (새창열림)
    SEOYUL
    SEOYUL
    산호는 해양 생물의 4분의 1에게 집을 제공하는 만큼 생태계에서 중요한 근간이라고 합니다. 하지만 최근 환경 파괴로 산호지대가 사라지고 있어 여러 국가에서 산호를 심는 활동을 한다고 합니다. 이와같이 꾸준한 내용으로 미래를 위한 블로그를 만들어가고자 합니다.

    티스토리툴바