본문 바로가기

Python/Intermediate

[Python] 병행성(Concurrency) - Coroutine

  • 흐름제어
    병행성(Concurrency) : 한 컴퓨터가 여러 일을 동시에 수행 -> 단일 프로그램안에서 여러일을 쉽게 해결
    병렬성(Parallelism) : 여러 컴퓨터가 여러 작업을 동시에 수행 -> 속도
    코루틴(Coroutine) : 단일(싱글) 스레드, 스택을 기반으로 동작하는 비동기 작업
    스레드 : OS 관리, cpu 코어에서 실시간, 시분할 비동기 작업 -> 멀티스레드
    yield, send : 메인 <==> 서브
    코루틴 제어, 상태, 양방향 전송
    yield from
    서브루틴 : 메인루틴에서 호출 -> 서브루틴에서 수행(흐름제어)
    코루틴 : 루틴 실행 중 중지 -> 동시성 프로그래밍
    코루틴 : 스레드에 비해 오버헤드 감소
    스레드 : 싱글스레드 -> 멀티스레드 -> 복잡 -> 공유되는 자원 -> 교착 상태 발생 가능성, 컨텍스트 스위칭 비용 발생, 자원 소비 가능성 증가
    3.5이상에서, def -> async, yield -> await
# 코루틴 Ex1
def coroutine1():
    print('>>> coroutine started.')
    i = yield
    print('>>> coroutine received : {}'.format(i))

# 제네레이터 선언
cr1 = coroutine1()

print('제너레이터 선언:', cr1, '~', type(cr1))

# 메인 루틴
# yield 지점 까지 서브루틴 수행
next(cr1) # "i = yield"까지 실행됨, 그래서 첫번째 print 출력됨

# 기본 전달 값 None
# next(cr1) # yield 한개라서 예외발생함

# 값 전송
print('--------------------')
cr1.send(100) # yield 에서 대기중일때만 send 가능능

--------------------------------------------[result]

제너레이터 선언: < generator object coroutine1 at 0x000002B8320447D8 > ~ < class 'generator' >
>>> coroutine started.
--------------------
>>> coroutine received : 100
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-80-c8586eb369c0> in <module>
     13 # 값 전송
     14 print('--------------------')
---> 15 cr1.send(100)

StopIteration: 

 

잘못된 사용 예시

 

# 잘못된 사용

cr2 = coroutine1()

cr2.send(100) # 예외 발생

--------------------------------------------[result]

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-120-902d001b274e> in <module>
      3 cr2 = coroutine1()
      4 
----> 5 cr2.send(100) # 예외 발생

TypeError: can't send non-None value to a just-started generator

 

코루틴 상태확인

 

# 코루틴 Ex2
# 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))
    yield x + y + z
    # return 77

cr3 = coroutine2(10)
print(cr3, type(cr3))


from inspect import getgeneratorstate

print('-------------')
print(getgeneratorstate(cr3))
print('-------------')
print(next(cr3))
print('-------------')
print(getgeneratorstate(cr3))
print('-------------')
print(cr3.send(15))
print('-------------')
print(getgeneratorstate(cr3))
print('-------------')
print(cr3.send(20)) # 예외

--------------------------------------------[result]

< generator object coroutine2 at 0x000002B8320565C8 > < class 'generator' >
-------------
GEN_CREATED
-------------
>>> coroutine started : 10
10
-------------
GEN_SUSPENDED
-------------
>>> coroutine received : 15
25
-------------
GEN_SUSPENDED
-------------
>>> coroutine received : 20
45

# 코루틴 Ex3
# StopIteration 자동 처리(3.5 -> await)
# 중첩 코루틴 처리
def generator1():
    for x in 'AB':
        yield x
    for y in range(1,4):
        yield y

t1 = generator1()
# print(list(t1))

print(next(t1))
print(next(t1))
print(next(t1))
print(next(t1))
print(next(t1))
# print(next(t1))

--------------------------------------------[result]

A
B
1
2
3

t2 = generator1()

print(list(t2))

--------------------------------------------[result]

['A', 'B', 1, 2, 3]

 

yield from 

 

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


t3 = generator2()
# print(list(t3))

print(next(t3))
print(next(t3))
print(next(t3))
print(next(t3))
print(next(t3))
# print(next(t3))

--------------------------------------------[result]

A
B
1
2
3