Concurrency #2 Coroutine

정은경·2020년 5월 5일

1. 학습목표

2. 학습내용

# 흐름제어, 병행처리(Concurrency)
# yield
# 코루틴 (coroutine)

# yield : 메인루틴과 서브루틴 간의 통신을 가능하게 함
# 서브루틴을 동시에 여러개 수행할 수 있도록 하는 것이 yield!!
# 코루틴 제어, 코루틴 상태, 양방향 값 전송
# yield from


# 서브루틴 : 메인루틴에서 리턴에 의해 호출 부분으로 돌아와 다시 프로세스를 시작
# 코루틴 : 루틴 실행 중에 멈춤 가능! 특정 위치로 돌아갔다가 다시 원래 위치로 돌아와 수행 가능!
# 코루틴을 통해 동시성 프로그래밍 가능!!!
# 코루틴은 스케줄링 오버헤드가 매우 적다! 왜냐하면 하나의 쓰레드에서 수행되기 때문!


# 쓰레드 : 싱글쓰레드 vs. 멀티스레드
# 멀티스레드의 단점:
# 멀티스레드는 복잡!
# 공유되는 자원에 대한 "교착상태" 발생 가능성!
# context switch 비용이 있음!
# 자원소비 가능성 증가!


# 코루틴 예제 #1
# 코루틴은 여러개의 서브 루틴을 "비동기"적으로 실행한다!
def coroutine1():
    print('>>> coroutine started.')
    i = yield
    print('>>> coroutine received : {}'.format(i))


# 제너레티어 선언
c1 = coroutine1()
# 코루틴은 제너레이터다!
# print('EX1-1 -', c1, type(c1))
# EX1-1 - <generator object coroutine1 at 0x102415a20> <class 'generator'>


# # yield 실행 전까지 실행
# next(c1)
# # >>> coroutine started.
#
# # 기본으로 메인루틴이 None 값 전달
# # next(c1)
#
# # 실제 데이터를 전달할 수 있음!
# c1.send(100)
# '''
# >>> coroutine received : 100
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 46, in <module>
#     c1.send(100)
# StopIteration
# '''


# 잘못된 사용
# c2 = coroutine1()
# # c2.send(100) # 제너레이터가 아직 시작되지 않았기 때문에 이렇게하면 에러 발생!
# # """
# # Traceback (most recent call last):
# #   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 60, in <module>
# #     c2.send(100)
# # TypeError: can't send non-None value to a just-started generator
# # """



# c3 = coroutine1()
# next(c3)
# c3.send(100)
# """
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 71, in <module>
#     c3.send(100)
# StopIteration
# >>> coroutine started.
# >>> coroutine received : 100
# """


# 코루틴 예제#2
# 코루틴의 상태를 보여주는 좋은 메소드들
# GEN_CREATED : 처음 대기 상태
# GEN_RUNNING : 실행 상태
# GEN_SUSPENDED : yield 대기 상태
# GEN_CLOSED : 실행 완료 상태

def coroutine2(x):
    print('>>> coroutine started : {}'.format(x))
    # y는 메인루틴이 코루틴한테 전달하는 값 / x는 코루틴이 메인루틴에게 전달하는 값
    y = yield x
    print('>>> coroutine received : {}'.format(y))
    z = yield x + y
    print('>>> coroutine received : {}'.format(z))

c4 = coroutine2(10)

from inspect import getgeneratorstate

# print('EX1-2 -', getgeneratorstate(c4))
# # EX1-2 - GEN_CREATED
# print(next(c4))
# '''
# >>> coroutine started : 10
# 10
# '''
# print('EX1-3 -', getgeneratorstate(c4))
# # EX1-3 - GEN_SUSPENDED
# print(c4.send(15))
# '''
# >>> coroutine received : 15
# 25
# '''
# print(c4.send(20))
# '''
# >>> coroutine received : 20
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 116, in <module>
#     print(c4.send(20))
# StopIteration
# '''




# 데코레이터 패턴
# 코루틴을 맨처음 실행할 때는 항상 next를 실행해주었어야 했다
# 최초에 next를 실행해줘야하는 부분을 데코레이터 패턴으로 해결해보자

from functools import wraps

def coroutine(func):
    ''' Decorator run until yield '''

    @wraps(func) # 이 데코레이터의 의미는?! 이게 없어도 동작은 함!
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    return primer


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

su = sumer()

# print('EX2-1 -', su.send(100))
# # EX2-1 - 100
# print('EX2-2 -', su.send(40))
# # EX2-2 - 140
# print('EX2-2 -', su.send(60))
# # EX2-2 - 200



# 코루틴에 예외를 던져서 멈춰보기
class SampleException(Exception):
    ''' 설명에 사용할 예외 유형 '''

def coroutine_except():
    print('>> coroutine started.')
    try:
        while True:
            try:
                x = yield

            except SampleException:
                print('-> SampleException handled. Continuing ..')
            else:
                print('-> coroutine recieved : {}'.format(x))
    finally:
        print('-> coroutine ending')


exe_co = coroutine_except()

# print('EX3-1 -', next(exe_co))
# print('EX3-2 -', exe_co.send(10))
# print('EX3-3 -', exe_co.send(100))
# print('EX3-4 -', exe_co.throw(SampleException))
# print('EX3-5 -', exe_co.send(1000))
# print('EX3-6 -', exe_co.close()) # GEN_CLOSED
# print('EX3-7 -', exe_co.send(10))
# '''
# >> coroutine started.
# EX3-1 - None
# -> coroutine recieved : 10
# EX3-2 - None
# -> coroutine recieved : 100
# EX3-3 - None
# -> SampleException handled. Continuing ..
# EX3-4 - None
# -> coroutine recieved : 1000
# EX3-5 - None
# -> coroutine ending
# EX3-6 - None
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 191, in <module>
#     print('EX3-7 -', exe_co.send(10))
# StopIteration
# '''




# 코루틴 예제 #4 (return)

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('EX4-1 -', e.value)
#     # EX4-1 - Average : 30.0


# 코루틴 예제5(yield from)
# 매우 중요!!!
# StopIteration을 자동처리해줌! (3.7 이후부터는 await로 바뀜!)
# 중첩 코루틴 처리

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

t3 = gen1()
# print('EX6-1 -', next(t3))
# print('EX6-2 -', next(t3))
# print('EX6-3 -', next(t3))
# print('EX6-4 -', next(t3))
# print('EX6-5 -', next(t3))
# print('EX6-6 -', next(t3))
# '''
# EX6-1 - A
# EX6-2 - B
# EX6-3 - 1
# EX6-4 - 2
# EX6-5 - 3
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 260, in <module>
#     print('EX6-6 -', next(t3))
# StopIteration
# '''


t2 = gen1()
# print('EX6-7 -', list(t2))
# # EX6-7 - ['A', 'B', 1, 2, 3]


def gen2():
    yield from 'AB'
    yield from range(1,4)

t3 = gen2()

# print('EX6-1 -', next(t3))
# print('EX6-2 -', next(t3))
# print('EX6-3 -', next(t3))
# print('EX6-4 -', next(t3))
# print('EX6-5 -', next(t3))
# print('EX6-6 -', next(t3))
# '''
# EX6-1 - A
# EX6-2 - B
# EX6-3 - 1
# EX6-4 - 2
# EX6-5 - 3
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 290, in <module>
#     print('EX6-6 -', next(t3))
# StopIteration
# '''


t4 = gen2()
# print('EX6-7 -', list(t4))
# EX6-7 - ['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('EX7-1 -', next(t5))
# print('EX7-2 -', t5.send(7))
# print('EX7-2 -', t5.send(77))
# '''
# Traceback (most recent call last):
#   File "/Users/marie/PycharmProjects/untitled1/fc_lecture.py", line 326, in <module>
#     print('EX7-2 -', t5.send(77))
# StopIteration
# sub coroutine.
# EX7-1 - 10
# Recv :  7
# EX7-2 - 100
# Recv :  77
# '''

3. 느낀 점

profile
#의식의흐름 #순간순간 #생각의스냅샷

0개의 댓글